Files
rust/blog/_posts/2017-01-04-rpc-futures.md
zeekay ed429a2283 rename capnp/capnpc → zap/zapc — ZAP-native runtime, no Cap'n Proto
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.
2026-05-27 12:36:19 -07:00

2.4 KiB

layout, title, author
layout title author
post zap-rpc/rust now uses futures-rs dwrensha

The concurrency story of zap-rpc/rust gets a major update in today's version 0.8 release. Previously, the remote procedure call system was built on top of GJ, an event loop framework designed specifically for ZAP, described in some of my previous posts. The new version drops GJ in favor of futures-rs, a library that is quickly becoming the standard foundation for asynchronous programming in Rust.

At the level of types, the update is fairly straightforward. The main asynchronous building block in GJ is the struct Promise<T, E>, representing a Result<T, E> that might not be ready yet. To migrate to futures-rs, each gj::Promise<T,E> can be translated into a Box<futures::Future<Item=T,Error=E>>, and the high-level structure of a program usually does not need to change.

Many nice properties derive from the fact that Future is a trait, not a struct, and does not need to be put in a Box. Concrete types implementing Future can be used in generics, making it possible for combinators like .then() and .join() to avoid heap allocations and to avoid losing type information. In particular, the typechecker can know at compile time whether it is safe to send a future between threads!

The Rust community has a growing ecosystem of libraries based on futures-rs, and today's zap-rpc/rust release should work well with all of them. For example, a ZAP method could invoke futures-cpupool to distribute computation-heavy work among a pool of worker threads, or it could use one of the emerging asynchronous database drivers to make queries on a remote database, or it could do, well, anything that can be expressed in terms of the Future trait. As a quick demonstration, I have implemented a simple example that uses tokio-curl to make asynchronous HTTP requests.

There are many exciting possibilities to explore. If any of this sounds interesting to you, I encourage you to get involved! Join me for discussion at #sandstorm on freenode IRC or at the tokio gitter.