Remoc

Remote multiplexed objects and channels for Rust.

Channels are how Rust programs talk between tasks. Remoc lets them talk between machines the same way. Send a channel to the other side like any other value and it is connected, inside the one link you already have. That link can be TCP, TLS, a WebSocket, a pipe or a serial line. On top of the channels you also get remote function calls, RPC on traits and observable collections.

$ cargo add remoc

crates.io docs.rs Apache 2.0 license

What it does

Remote channels

MPSC, oneshot, watch and broadcast, with the API you already know from Tokio. They all share one transport and messages are sent in chunks, so a big blob on one channel does not stall the others.

rch module →

Channels are values

Put a sender into a struct, an enum or another channel and send it away. The new channel is set up for you inside the connection that is already open. No extra port to allocate, no service registry, no name lookup.

Remote trait calls

Write a trait, put #[rtc::remote] on it and you get a client and a server implementation. Arguments and return values may contain channels, so a method can hand back a stream of updates instead of a single value.

rtc module →

Remote functions

Send a closure over and let the other side call it. The arguments travel one way, the result comes back.

rfn module →

Observable collections

Hash maps, b-tree maps, vectors and sets that publish what happens to them. A subscriber first gets a snapshot and then every change as it occurs.

robs module →

Remote objects

Locks and read/write locks that also work when the other party is on a different machine, plus values that are only fetched once somebody actually looks at them.

robj module →

Bring your own transport

Remoc does no networking. Give it an AsyncRead and AsyncWrite pair, or a Sink and Stream of packets, and you are connected. There are worked examples for TCP, TLS, WebSockets, a child process over pipes and links that survive a broken connection.

transports →

Any Serde format

Postbag, Bincode, CBOR, JSON, MessagePack or Postcard, chosen per connection. Pick a self-describing one and old and new versions of your software keep understanding each other.

100 % safe Rust

Not a line of unsafe, built on Tokio. It also compiles for wasm32-unknown-unknown and WASI, so a browser tab can be one end of the connection.

A small example

The client wants the server to count for it. It creates a channel, puts the sender into the request and reads the numbers from the receiver. Nothing else has to be arranged, both channels run through the single TCP connection that was opened at the start.

Shared
use remoc::prelude::*;

#[derive(Serialize, Deserialize)]
struct CountReq {
    up_to: u32,
    // A sender, on its way to the peer.
    seq_tx: rch::mpsc::Sender<u32>,
}
Client
let (seq_tx, mut seq_rx) = rch::mpsc::channel(1);
tx.send(CountReq { up_to: 4, seq_tx }).await?;

// The channel is live now, nothing to set up.
while let Some(i) = seq_rx.recv().await? {
    println!("{i}");
}
Server
while let Some(req) = rx.recv().await? {
    for i in 0..req.up_to {
        req.seq_tx.send(i).await?;
    }
}
Or call a trait remotely
#[rtc::remote]
trait Counter {
    async fn value(&self)
        -> Result<u32, rtc::CallError>;

    async fn watch(&mut self)
        -> Result<rch::watch::Receiver<u32>, rtc::CallError>;
}

The whole thing, client and server →