Skip to content

Why Zig

Docs-site note (2026-06-25): This page argues the language choice — why Vexor's consensus client is written in Zig rather than Rust (Agave) or C (Firedancer), including the honest costs. It is a rationale page; for what is built and how, see Architecture. Claims are grounded; tradeoffs are stated plainly, not waved away.


The decision

Vexor's entire consensus path is written in Zig — a single, clean-room codebase with no Rust FFI. The only native code linked is Firedancer's AVX-512 leaf crypto (the ballet ed25519 / blake3 / bn254 routines), pulled in through Zig's C interop. Everything else — replay, the runtime, the sBPF VM, gossip, repair, networking, the blockstore — is Zig.

The two canonical Solana clients made different choices: Agave is Rust, Firedancer is C (with a Rust Agave runtime alongside it). Choosing Zig was a deliberate bet that a small, explicit, modern systems language gives the best balance of control, safety, and auditability for a clean-room client — at the cost of a younger ecosystem we manage carefully.


What Zig gives us

  • A small, explicit language. Zig is deliberately tiny: no hidden control flow, no operator overloading, no exceptions, explicit allocators everywhere. For a consensus client — where you must be able to reason about exactly what every line does to the bytes — a language with few surprises is a feature, not a limitation.
  • Manual, explicit memory management. No garbage collector, full control over layout and lifetime. This matters directly for Vexor's tile-to-core topology, where each thread has a declared core home and we control exactly what is allocated where and when — there is no runtime deciding for us.
  • comptime instead of macros. Zig runs ordinary Zig code at compile time for metaprogramming, generics, and code generation. It is the same language you already read — not a separate macro dialect — so a serializer, a key encoder, or a generic container is auditable line by line, with no macro-expansion layer to mentally unfold.
  • First-class C interop. Zig can @cImport C headers and link C objects with no FFI boilerplate or binding layer. This is not theoretical for Vexor: we link Firedancer's hand-tuned AVX-512 leaf crypto directly, getting byte-identical, audited crypto primitives without rewriting them and without a foreign-function shim.
  • A single, readable, auditable codebase. One language, one toolchain, one mental model from the networking layer to the VM. An auditor reviewing Vexor reads Zig the whole way down — not Rust here, C there, and a macro DSL in between. For a clean-room client whose value depends on being independently checkable, a unified codebase is a core design goal, not an aesthetic preference.

Zig vs Rust (Agave) and C (Firedancer)

The honest framing is per-comparison — Zig's advantage over Rust is different from its advantage over C, and some properties are simply table stakes for any serious systems language.

Property Zig Rust (Agave) C (Firedancer)
No garbage collector ✅ (table stakes for the class)
Memory safety model Manual + safety-checked builds, defer Borrow checker (strongest) Manual, fewest guardrails
Language size / complexity Small, explicit Large, complex surface Small, but many footguns
Metaprogramming comptime (plain Zig) Macros (separate dialect) Preprocessor (text macros)
C interop First-class, no bindings FFI / unsafe + bindgen Native
Single-language consensus path Mixed (C + Rust runtime)
Ecosystem maturity Younger, pre-1.0 Mature Mature

Versus Rust (Agave): the edge is not memory management — Rust has no GC either, and its borrow checker is the strongest static memory-safety model of the three. The edge is simplicity and directness. Rust is a large language with a heavy type and trait system and a macro layer; Zig is small enough to hold in your head, uses comptime (ordinary Zig) where Rust uses macros, and makes every allocation explicit — which suits a codebase whose correctness must be auditable byte by byte and whose memory we want to place by hand against a fixed core topology.

Versus C (Firedancer): Zig keeps C's smallness and control but removes a long list of footguns — safety-checked builds, defer for cleanup, slices instead of raw pointer+length pairs, no preprocessor, and modern tooling (built-in build system, test runner, cross-compilation). We still get C's strengths where they matter: we link Firedancer's C leaf crypto directly through Zig's C interop, so we inherit the fast, audited primitives without inheriting C's hazards across the rest of the codebase.


The honest tradeoffs

Choosing a younger language is a real cost, and we manage it deliberately rather than pretend it away.

  • Pre-1.0 language churn. Zig is not yet 1.0, and releases can change the standard library and syntax in breaking ways (recent versions reworked the file Reader/Writer APIs and made ArrayList unmanaged by default, among others). Vexor's mitigation is to pin a single toolchain — Zig 0.15.2 — for all builds and deploys, so the language never moves underneath a build. Upgrades are deliberate, tested events, not incidental drift.
  • A smaller library ecosystem. There is far less off-the-shelf Zig than Rust or C. For a clean-room consensus client this cuts both ways: we write more ourselves, but a client whose goal is a single auditable codebase wants to own its critical paths rather than pull in large third-party dependencies. Where a mature native implementation already exists and is the canonical one — the leaf crypto — we link it via C interop instead of reimplementing it.
  • A smaller community and tooling surface. Fewer libraries, fewer Stack Overflow answers, fewer ready-made integrations than Rust. We accept this as the price of the control and auditability the language gives back.

These costs are real. The bet is that for this kind of software — a clean-room, independently auditable, byte-faithful consensus client with an explicit memory and core model — the control, simplicity, and single-language auditability are worth them.


See also