| Crates.io | capnp-rpc |
| lib.rs | capnp-rpc |
| version | 0.25.0 |
| created_at | 2014-11-20 19:41:48.635605+00 |
| updated_at | 2026-01-09 13:32:47.010949+00 |
| description | implementation of the Cap'n Proto remote procedure call protocol |
| homepage | |
| repository | https://github.com/capnproto/capnproto-rust |
| max_upload_size | |
| id | 128 |
| size | 747,009 |
This is a level one implementation of the Cap'n Proto remote procedure call protocol. It is a fairly direct translation of the original C++ implementation.
First, make sure that the
capnp executable
is installed on your system,
and that you have the capnpc crate
in the build-dependencies section of your Cargo.toml.
Then, in a file named foo.capnp, define your interface:
@0xa7ed6c5c8a98ca40;
interface Bar {
baz @0 (x :Int32) -> (y :Int32);
}
interface Qux {
quux @0 (bar :Bar) -> (y :Int32);
}
Now you can invoke the schema compiler in a
build.rs file, like this:
fn main() {
::capnpc::CompilerCommand::new().file("foo.capnp").run().unwrap();
}
Such a command generates a foo_capnp.rs file in the OUT_DIR
directory provided by cargo.
To import the generated code, add a line like this at the root of your crate:
capnp::generated_code!(pub mod foo_capnp);
(If you want to import the code at a non-toplevel module location, then you will
need to use the $Rust.parentModule annotation, defined in rust.capnp.)
For each defined interface, the generated code includes a Client struct
that can be used to call the interface's methods. For example, the following
code calls the Bar.baz() method:
fn call_bar(client: ::foo_capnp::bar::Client)
-> Box<Future<Item=i32, Error=::capnp::Error>>
{
let mut req = client.baz_request();
req.get().set_x(11);
Box::new(req.send().promise.and_then(|response| {
Ok(response.get()?.get_y())
}))
}
A bar::Client is a reference to a possibly-remote Bar object.
The Cap'n Proto RPC runtime tracks the number of such references
that are live at any given time and automatically drops the
object when none are left.
The generated code also includes a Server trait for each of your interfaces.
To create an RPC-enabled object, you must implement that trait.
struct MyBar {}
impl ::foo_capnp::bar::Server for MyBar {
async fn baz(
self: Rc<Self>,
params: ::foo_capnp::bar::BazParams,
mut results: ::foo_capnp::bar::BazResults)
-> Result<(), ::capnp::Error>
{
results.get().set_y(params.get()?.get_x() + 1);
Ok(())
}
}
Then you can convert your object into a capability client like this:
let client: foo_capnp::bar::Client = capnp_rpc::new_client(MyBar {});
This new client can now be sent across the network.
You can use it as the bootstrap capability when you construct an RpcSystem,
and you can pass it in RPC method arguments and results.
The methods of the generated Server traits return
a value of type impl Future<Output = Result<(), ::capnp::Error>>.
As you have seen above,
these can be implented as async fn methods returning Result<(), ::capnp::Error>.
The RPC response will be sent back to the method's caller once two things have happened:
Results struct has been dropped.Future has resolved.Usually (1) happens before (2).
Here's an example of a method implementation that does not return immediately because it awaits another request:
struct MyQux {}
impl ::foo_capnp::qux::Server for MyQux {
async fn quux(
self: Rc<Self>,
params: ::foo_capnp::qux::QuuxParams,
mut results: ::foo_capnp::wux::QuuxResults)
-> Result<(), ::capnp::Error>
{
// Call `baz()` on the passed-in client.
let bar_client = params.get()?.get_bar()?;
let mut req = bar_client.baz_request();
req.get().set_x(42);
let response = req.send().promise.await?; // <-- await
results.get().set_y(response.get()?.get_y());
Ok(())
}
}
It's possible for multiple calls of quux() to be active at the same time
on the same object, and they do not need to return in the same order
as they were called.