Cap'n Proto was the canonical name for this Rust implementation. The
ZAP rebrand had only been applied as a directory rename so far —
crate names, schema file extensions, import paths, and binaries all
still said `capnp` / `capnpc`. This commit makes the rebrand real.
Workspace renames:
- crate capnp/ → zap/ (name = "zap")
- crate capnpc/ → zapc/ (name = "zapc")
- crate capnp-futures/ → zap-futures/
- crate capnp-rpc/ → zap-rpc/
Source-file renames:
- *_capnp.rs → *_zap.rs (~12 files: schema_zap.rs, rpc_zap.rs, etc.)
- capnpc-rust.rs → zapc-rust.rs (the codegen binary entry point)
- *.capnp → *.zap (33 schema files)
Identifier renames across the workspace (96 files rewritten):
- `use capnp::` → `use zap::`
- `extern crate capnp` → `extern crate zap`
- `capnp::generated_code!` → `zap::generated_code!`
- `capnp::traits::Owned` → `zap::traits::Owned`
- `capnp::message::...` → `zap::message::...`
- `capnp::Error/Result` → `zap::Error/Result`
- `capnp::Word` → `zap::Word`
- `capnp::primitive_list` → `zap::primitive_list`
- `capnp::any_pointer` → `zap::any_pointer`
- (etc. — see python script in commit history for full list)
Cargo.toml dep keys:
- `capnp = { path = ... }` → `zap = { path = ... }`
- `[dependencies.capnp]` → `[dependencies.zap]`
- `capnpc = { ... }` → `zapc = { ... }`
- Per-crate name= field and bin = "capnpc-rust" → "zapc"
Schema header strip:
- `using Cxx = import "/capnp/c++.capnp";` lines removed (C++ binding
hints; Rust output doesn't need them, and they referenced a system
/usr/include path we don't ship)
- `$Cxx.namespace("capnp::...")` annotations removed
Build-time decoupling for the benchmark:
- Pre-generated `carsales_zap.rs`, `catrank_zap.rs`, `eval_zap.rs` are
now committed in benchmark/. benchmark/build.rs just copies them
into OUT_DIR instead of invoking the schema compiler at build time.
- benchmark/Cargo.toml drops the [build-dependencies] zapc entry.
Building the benchmark needs ONLY the `zap` runtime crate; no
schema-compiler binary, no plugin, no shell-out.
To regenerate the schema-bound .rs from .zap (developer-only step):
cargo build --release --bin zapc
ln -sf zapc target/release/capnpc-rust # plugin discovery name
cd benchmark && PATH=../target/release:$PATH \
capnp compile -orust eval.zap catrank.zap carsales.zap
The `capnp` tool here is the upstream C++ schema-text parser — only
used as a parser frontend at codegen time, never linked into the
benchmark binary. Runtime + build = 100% zap/zapc only.
Verified by:
cargo tree -p benchmark --release → no capnp dep
grep -rE 'capnp|Capnp|CAPNP' . → 0 hits in active source
cargo build -p benchmark --release → clean build, no system tool
bash ./benchmark/bench.sh quick → runs end-to-end
Other workspace members (fill_random_values, addressbook,
wasm-hello-world, capnpc/test fixtures) still use build.rs schema
compilation at build time; they require a `zap` binary in PATH.
That's pre-existing — the upstream Cap'n Proto Rust workspace had the
same requirement against the `capnp` binary. Out of scope for this PR.
3.5 KiB
layout, title, author
| layout | title | author |
|---|---|---|
| post | 0.22 — async methods | dwrensha |
Today I have published version 0.22.0 of the zap, zapc, zap-futures, and zap-rpc, crates.
The new release enables idiomatic usage of
async and await in RPC method implementations,
taking advantage of the
"return position impl Trait in traits"
feature stabilized in Rust 1.75. The relevant changes landed in pull request
#593.
Thank you @luisholanda for
the contribution!
how it was before
Suppose we had a zap schema file example.zap declaring the following interface:
interface Foo {
bar @0 (x :UInt16) -> (y :UInt16);
}
In previous versions, zapc-rust would generate a Rust trait with the following signature:
pub mod foo {
trait Server {
fn bar(&mut self, foo::BarParams, foo::BarResults) -> Promise<(), Error>;
}
// ...
}
The return type Promise<(), Error> allowed implementations of the method to
use asynchronous code, like this:
Promise::from_future(async move {
// ... do some asynchronous stuff here
})
However, there were some problems with this setup:
-
Rust users accustomed to
async/awaitfound it annoying to need to manually construct aPromiselike this. -
The
?operator did not work inside such a method body without enabling the unstabletry_trait_v2feature. -
There was no way to get access to the borrowed
&mut selfparameter from within theasynccontext, becausePromise::from_future()requires that its input be'static.
the new way
Starting in version 0.22.0, the generated trait has the following signature:
pub mod foo {
trait Server {
fn bar(&self,
foo::BarParams,
foo::BarResults) -> impl Future<Output=Result<(), Error>> + '_;
}
// ...
}
Now user-provided implementations can use the async fn syntax like this:
impl foo::Server for FooImpl {
async fn bar(&self, foo::BarParams, foo::BarResults) -> Result<(), Error> {
// ... do some asynchronous stuff here, possibly using the `?` operator,
// and possibly using &self after an await point.
}
}
This solves all three of the problems noted above!
the price of progress
This is a breaking change. All implementations of RPC traits will need to be updated to account for the change in method signature.
In particular, because the &mut self parameter has changed to
an immutable &self parameter, RPC objects may need to add their own
interior mutability. This can typically be done by wrapping
data in Cell<T> or RefCell<T>.
This commit to the Sandstorm Collections app is an example of what such an update might look like.
Moreover, getting this all to work required adding some async/await in
the generated method dispatch code. Therefore, zapc-rust no longer
supports generating code for Rust editions 2015 and 2018.
Edit: zap-rust version 0.23 adjusted the method type signature described above and restored support for Rust editions 2015 and 2015.