rapace 0.1.0

High-performance RPC framework with shared memory transport
Documentation

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:

[dependencies]
rapace = { version = "0.1", features = ["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),
}