Xo is experimental. The source is now public on GitHub, or try Xo in your browser.
Menu · Xo and Rust

Docs

Xo and Rust

On this page

Part of the comparison series that starts with Xo and Go. It is not a “which is better” page. Rust and Xo both reject whole classes of bugs at compile time and both compile to native code without a garbage collector, but they make different trades: Rust gives the programmer control over memory and aliasing, Xo takes that control away in exchange for a smaller language. Rust is also mature and production proven; Xo is experimental. This page says where each is the better choice and what the measurements show, including the many rows where Rust is faster.

Versions. Claims about Rust are checked against Rust’s own documentation (the book, the reference, the standard library and Cargo docs) as of Rust 1.99.1 (the newest release, 2026-10-15), which is also the Rust the measurements used. The Xo in the measurements is a build of main between 0.26.0 and 0.27.0 (it reports itself as 0.26.0); they will be rerun with the released 0.27.0. Last verified: 2026-10-23.

Short version

Choose Rust when you need the fastest code for compute and floating point work, the smallest memory footprint, control over allocation and layout, no runtime at all (embedded, kernels, libraries called from C), many targets, a large ecosystem (crates.io, tokio, serde), or a stable language with a long compatibility record. In the measurements below Rust was faster on most compute rows, on JSON, on locks, used a quarter of Xo’s memory as a web server, and made binaries about half the size of Xo’s release binaries.

Consider Xo when you want much of the same compile-time safety (no null, no unhandled errors, exhaustive matches, no data races) without lifetimes, borrowing rules, or choosing an async runtime; when you want a dependency’s capabilities limited and recorded; when integer overflow should fault in every build; and when much of the code is written by AI agents working from structured compiler feedback. As a web server Xo served more requests per second than axum on tokio in the measurements below, with lower tail latency, but used more CPU per request.

What each is for

  • Rust: “A language empowering everyone to build reliable and efficient software”, “with no runtime or garbage collector”, whose “type system and ownership model guarantee memory-safety and thread-safety” (rust-lang.org).
  • Xo: backends, tools, and web services, designed so AI agents can write, check, cross compile, and debug programs with fast, structured feedback (Why Xo?).

The language

RustXo
Absent valuesNo null; Option<T>No null; T? is Option[T], and the compiler makes you handle None (core 2.5)
ErrorsResult<T, E> and ?. Result is #[must_use]: ignoring one is a warning by default, not an error (std::result)T ! E; an unhandled Result does not compile; ? propagates and records an error trace (core 2.6, 11.1)
Sum typesEnums with data and exhaustive matchEnums with payloads and exhaustive match, with guards (core 2, 3)
GenericsGenerics with trait bounds, monomorphizedGenerics with constraints (core 2.7)
Values and aliasingOwnership and borrowing: one mutable reference or many shared ones, checked by the borrow checker, with lifetimes in signatures where neededValue semantics with copy-on-write; no references to annotate; mutation is visible in signatures (var parameters) (core 5)
ConcurrencyThreads in std; async/await in the language, but “Most languages that support async bundle a runtime, but Rust does not” (the book, 17.1); tokio is the usual choiceTasks in the language with one built-in scheduler; structured concurrency (scope), cancellation without a parameter, channels and select (core 7)
Side effectsNot in signaturesEffects in signatures (uses (net, fs)), capability values passed from main, and fakes for tests (core 6)
Escape hatchunsafe blocks for raw pointers, FFI and other operations the compiler cannot checkNo raw pointers in Xo code; C and Go code through use c and use go, which xo audit lists (Dependencies)

Memory

  • Rust frees memory when its owner goes out of scope, with no collector. Shared ownership uses Rc or Arc (reference counting), and “Rust allows memory leaks by using Rc<T> and RefCell<T>”: a cycle is never freed (the book, 15.6).
  • Xo native builds use reference counting for every heap value: a value is freed when its last reference goes, with no collector pauses. Value semantics leave no cycles to leak. HTTP handlers get request scoped arenas by default (FAQ, core 10).
  • The difference in practice: Rust moves and borrows without touching a count, so most Rust code pays nothing; Xo pays for counts as values move. xo vet --cost points at the loops where that, or a copy-on-write copy, is expensive. In the measurements below Rust’s programs used less memory on most rows (Xo’s runtime and scheduler start larger), and Xo’s used less on the rows that build large collections (sieve, sort, strings).

Safety and security

RustXo
Memory safetySafe Rust is memory safe; unsafe code can break it, and the guarantee depends on every unsafe block in the program and its dependencies being correctMemory safe without an unsafe keyword; C reached through use c is outside the guarantee and listed by xo audit
Data races“Safe Rust guarantees an absence of data races”, but “Rust does not prevent general race conditions” (deadlocks, wrong ordering) (Rustonomicon)Values never alias mutably, so data races are impossible except through Mutex and Atomic, which are synchronized (core 5); race conditions are possible too, and xo test --explore=N runs N seeded schedules to find them
Integer overflowPanics in debug builds; in release builds (--release) “Rust performs two’s complement wrapping” unless overflow-checks is set (the book, 3.2)Faults in every build: a bug report, not a wrong number (core 2, 8); wrapping is explicit (wrapping_add, std 2)
CapabilitiesAny crate can open files, sockets, or processes; a build script (build.rs) is compiled and run “just before building the package” with the user’s permissions (Cargo book)Code reaches the file system, network, environment, or processes only through capability values it was given; a module can be held to a ceiling (uses none); no dependency code runs at fetch or build setup: no build scripts, no install hooks (core 6, decision 0156)
Supply chainCargo.lock pins versions and records checksums; the RustSec advisory database and cargo audit report known vulnerabilitiesxo.lock hashes each module version, plus an effect lock: an update that adds an effect fails until accepted, and xo audit lists each dependency’s effects, escape hatches, and vouches (Dependencies). There is no vulnerability database yet

Compiler and toolchain

Builds and binaries (the CRUD server of the benchmarks below and a hello program; Rust with cargo build --release and the twins’ profile, as the measured binaries were built; Go for reference; one build each, Apple M4 Pro, 2026-10-24):

Rust 1.99.1Go 1.26.1xo 0.29.0
Cold build, CRUD server7.6 s2.9 s2.3 s
Cold build, hello1.3 s1.4 s1.1 s
Warm build, nothing changed (CRUD / hello)0.03 / 0.01 s0.08 / 0.03 s0.03 / 0.03 s
Build after a one-function edit (CRUD / hello)2.9 / 1.2 s0.24 / 0.09 s0.50 / 0.28 s
Peak build memory, cold CRUD build410 MB311 MB992 MB
Binary size, shipping flags (CRUD / hello)1.12 / 0.41 MB (cargo build --release)5.48 / 1.57 MB (-ldflags="-s -w")2.28 / 0.67 MB (xo build --release)
Binary size, plain build (CRUD / hello)1.12 / 0.41 MB (no debug info)8.03 / 2.36 MB (with DWARF)6.23 / 1.90 MB (with DWARF)
Hello startup, wall2.5 ms2.8 ms2.6 ms

Source: twins-build-2026-10-24.md, lines 20 to 25, 39 to 48 and 58 to 65. Rust’s binaries are the smallest, by a factor of 1.6 to 2 against Xo’s --release binaries (4.6 to 5.6 against the plain xo build, which keeps 4.1 MB of debug information in the CRUD server; Go’s plain build keeps its DWARF too), and its no-op build is the fastest. Its cold build of the server is the slowest, because it compiles axum, hyper, tokio and serde from source, and a release build does not compile incrementally, so an edit rebuilds the crate (a debug build, which is incremental, was not measured). Xo builds use the most memory. Go’s build after a one-function edit (0.24 s) is faster than Xo’s (0.50 s). Xo’s hello started in 1.04 of Rust’s time with 1.4 times its memory (2.6 MB against 1.8).

  • Targets. Rust lists 8 Tier 1 host targets (“guaranteed to work”) and many more at Tier 2 and 3 (platform support); cross compiling adds a target with rustup and usually needs a linker for it. Xo: xo build --targets=all builds linux/amd64, linux/arm64, darwin/amd64, darwin/arm64, windows/amd64, and wasip1/wasm from any host with its own backends and linkers, no C toolchain (core 11).
  • Tests and fuzzing. Rust has cargo test, doc tests, and fuzzing through external crates (cargo-fuzz). Xo has test blocks, property tests, deterministic concurrency tests (xo test --explore=N), and contract fuzzing of requires/ensures contracts, on the Go backend only today (core 9).
  • Formatting and lints. Both have one formatter (rustfmt, xo fmt); Rust has Clippy, Xo has xo vet.

Writing it with AI agents

Rust is far better known: models have seen much more Rust than Xo, and rust-analyzer and the compiler’s error messages are excellent. The cost for an agent is the borrow checker and lifetimes, which can take several rounds to satisfy. Xo is designed for the agent’s loop:

  • xo check --json reports every problem with a stable XOnnnn code and machine-applicable fixes; codes never change meaning (diagnostics).
  • Rust habits (Vec<T>, vec![], impl blocks, .unwrap(), &self, format!, as casts, and others, 24 cases) get a diagnostic that names Rust and gives the Xo form, with a fix.
  • xo def, xo refs, xo query, xo edit, and xo mcp answer semantic questions and make edits through resolved symbols.

Benchmarks

Method. The same programs written twice, each in its own language’s idiom (Rust: plain loops, sort_unstable, tokio tasks and std threads, serde_json, axum on tokio), and run under xo bench --ab with Rust as A and Xo as B, 10 interleaved rounds; a verdict is “better” or “worse” only when the 95% interval excludes 1.0, and the file asks that ratios within 10% be read as ties. Every program prints a checksum and the run stops if Rust’s and Xo’s differ. Machine: Apple M4 Pro (12 cores), macOS 27, darwin/arm64, with other jobs running (each row waited for a load under 5). Versions: rustc 1.99.1 with opt-level 3, one codegen unit, thin LTO and panic = "abort"; axum 0.8.9, tokio 1.54.1, serde_json 1.0.154; Xo from main between 0.26.0 and 0.27.0, native backend. Date: 2026-10-23. Ratios are Xo over Rust; under 1.0 means Xo took less (rust-vs-xo-2026-10-23.md, lines 1 to 12 and 146 to 151).

Compute

workloadwall time, Xo / Rustverdictsource
strings0.44betterline 47
sort0.93betterline 32
sieve0.99within noiseline 22
interpreter1.22worseline 37
fib1.31worseline 17
collatz1.42worseline 42
n-body (floating point)2.23worseline 27

Peak memory: Xo used about 1.4 times Rust’s on fib, nbody, interp and collatz (2.6 MB against 1.8 MB), and 0.28 to 0.47 of Rust’s on sieve, sort and strings (lines 20 to 50).

Concurrency (Rust on tokio’s default multi thread runtime and std threads; the three async rows are also given on tokio’s current thread runtime, the Rust idiom for many small tasks that never need a second thread)

workloadwall time, Xo / Rustverdictsource
ping-pong (multi thread runtime)0.033betterline 65
fanout (multi thread runtime)0.87betterline 60
parallel map (std threads)1.27worseline 80
spawn (multi thread runtime)1.39worseline 55
ping-pong (current thread runtime)1.49worseline 95
mutex1.98worseline 70
rwmutex2.48worseline 75
fanout (current thread runtime)2.62worseline 90
spawn (current thread runtime)3.31worseline 85

The large ping-pong win is tokio’s multi thread runtime waking another worker on every hop; on the current thread runtime, which is how a Rust programmer would write that program, Rust is faster. Xo used more CPU than Rust on spawn (2.3 times) and parallel map (1.33 times), less on multi thread ping-pong, and 1.1 to 3.4 times Rust’s peak memory on every concurrency row (lines 55 to 98).

JSON (serde_json against Xo’s std/json; 1,000 users, 100 rounds)

workloadwall time, Xo / Rustverdictsource
decode1.54worseline 103
encode1.53worseline 113

For scale, the same file’s Go column: Xo took 0.36 of Go’s encoding/json time to decode and 0.71 to encode (lines 108 and 118).

HTTP: the users CRUD API (axum on tokio with serde_json, against Xo’s std/http with request arenas), 32 workers on loopback, 10 s measured after a 2 s warmup, the median of 3 runs (lines 137 to 143):

Rust (axum)XoXo / Rust
requests/s117,564135,4521.15
p50 latency213 µs205 µs0.96
p99 latency1,193 µs823 µs0.69
p99.9 latency5,578 µs1,786 µs0.32
server CPU per request17.61 µs21.14 µs1.20
peak RSS5.0 MB18.7 MB3.7

Xo served more requests in each of the three runs (lines 128 to 135), but axum used less CPU per request and about a quarter of Xo’s memory. The runs’ spread is wide (the third reached about half the rate of the first two, for every server), so read the throughput ratio as “about the same to somewhat faster”, not as 15%.

These are a young compiler’s numbers on one machine; the slower rows are the work queue (floating point, locks, task spawn, JSON). The comparison is rerun from bench/perf/rust/run.sh and this page’s tables are refreshed from its results file.

↑ index