rapace: High-performance RPC framework with shared memory transport.
Quick Start
Define a service using the #[rapace::service] attribute:
use rapace::prelude::*;
#[rapace::service]
trait Calculator {
async fn add(&self, a: i32, b: i32) -> i32;
async fn multiply(&self, a: i32, b: i32) -> i32;
}
// Server implementation
struct CalculatorImpl;
impl Calculator for CalculatorImpl {
async fn add(&self, a: i32, b: i32) -> i32 {
a + b
}
async fn multiply(&self, a: i32, b: i32) -> i32 {
a * b
}
}
The macro generates CalculatorClient<T> and CalculatorServer<S> types.
Sessions
Each transport half must be wrapped in an [RpcSession] that you own:
let (client_transport, server_transport) = rapace::InProcTransport::pair();
let client_session = Arc::new(rapace::RpcSession::with_channel_start(Arc::new(client_transport), 2));
let server_session = Arc::new(rapace::RpcSession::with_channel_start(Arc::new(server_transport), 1));
tokio::spawn(client_session.clone().run());
tokio::spawn(server_session.clone().run());
server_session.set_dispatcher(move |_channel_id, method_id, payload| {
let server = CalculatorServer::new(CalculatorImpl);
Box::pin(async move { server.dispatch(method_id, &payload).await })
});
let client = CalculatorClient::new(client_session.clone());
let result = client.add(2, 3).await?;
Pick distinct starting channel IDs (odd vs. even) when both peers initiate RPCs on the same transport.
Streaming RPCs
For server-streaming RPCs, return Streaming<T>:
use rapace::prelude::*;
#[rapace::service]
trait Numbers {
async fn range(&self, start: i32, end: i32) -> Streaming<i32>;
}
Transports
rapace supports multiple transports:
- mem (default): In-memory transport for testing
- stream: TCP/Unix socket transport
- websocket: WebSocket transport for browser clients
- shm: Shared memory transport for maximum performance
Enable transports via feature flags:
[]
= { = "0.1", = ["stream", "shm"] }
Error Handling
All RPC methods return Result<T, RpcError>. Error codes align with gRPC
for familiarity:
use rapace::prelude::*;
match client.add(1, 2).await {
Ok(result) => println!("Result: {}", result),
Err(RpcError::Status { code: ErrorCode::InvalidArgument, message }) => {
eprintln!("Invalid argument: {}", message);
}
Err(e) => eprintln!("RPC failed: {}", e),
}