Skip to content

Rust

For trusted, performance-critical Particles you can author in Rust. Instead of one call per row, the server exchanges whole Arrow RecordBatches over the Apache Arrow C Data Interface — one call per batch, with zero-copy of the data (Arrow buffers are shared, not serialized). On a tight loop that is the fastest tier.

The contract is the same as Python: your Rust function’s signature is the SQL signature, STRICT-null is honoured, and the same naming rules apply.

  • Hot paths only. Batch-at-a-time Arrow exchange plus native code makes this the fastest tier when a Particle is on a tight inner loop. For everything else, Python is simpler and quick to iterate on.
  • Trusted code only. A compiled Rust Particle runs with the engine’s full OS authority (operator-vetted, digest-pinned). Untrusted / multi-tenant Particles are the domain of the (future) sandboxed WASM tier, not this one.

You write only the function body; the SDK owns the Arrow FFI shim (the ABI discovery symbols and the panic-guarded, batch entrypoint). The example crates live at examples/particles/rust/:

/// domain_of(text) -> text — the domain part of an email.
fn domain_of(email: &str) -> &str {
email.rsplit('@').next().unwrap_or("")
}

The crate is a standalone cdylib:

[lib]
name = "domain_of"
crate-type = ["cdylib"]
[dependencies]
particle-abi = { path = "…/crates/particle-abi" } # shared C-ABI structs
arrow = { version = "53", features = ["ffi"] } # same Arrow major as the host
Terminal window
cd examples/particles/rust/domain_of
cargo build --release
# -> target/release/libdomain_of.dylib (macOS)
# target/release/libdomain_of.so (Linux)

Start a server built with the Rust tier, then load the built artifact the same way you load any Particle (Loading) — the tier is selected automatically from the artifact, so once it’s loaded you just call it from SQL:

Terminal window
cargo build --release --bin spanner --features spanner-exec/particles-rust
particledb start --no-auth --data-dir /tmp/pdb_rust --pg-addr 0.0.0.0:5432
SELECT domain_of(email) FROM users; -- 'kyle@jepsen.io' -> 'jepsen.io'

The Rust tier honours the same contracts as the Python tier: STRICT-null (a NULL argument yields NULL without invoking the body), immutable volatility by default, deny-by-default capabilities, and the built-in collision rule. The end-to-end regression (crates/exec/tests/particles_rust_cdylib_regression.rs) builds each example, loads it, and asserts the SQL result value — e.g. domain_of('kyle@jepsen.io') returns jepsen.io.