Why Rust, after years of Go
Most of the systems software I'd written before frostvex was in Go. I like Go. Rolled-out tooling, fast compile times, predictable runtime behavior, an ecosystem deep enough to find a maintained library for almost anything. The fact that frostvex is in Rust is a deliberate departure that took me a while to commit to.
This post is the case I made to myself in late 2024. With about a year of frostvex's commit history behind me, I think it still holds up.
The two reasons
Memory model. Frostvex's hot path is lots of small allocations — chunk hashes, manifest entries, peer state. With Go, the GC pauses on a 100k-file pool were measurable. They didn't break anything; they did make the p99 numbers harder to reason about. Rust gives me deterministic allocation and freedom from "wait, is this allocation going to provoke a collection?"
FFI and binary size. I knew I'd want to embed the BLAKE3 reference implementation, which has SIMD intrinsics that you can't easily get to from Go. And I wanted a small static binary — Go binaries are 8–12 MB out of the box, which is fine but not nice. Rust with LTO and stripped symbols clocks in at ~3 MB for frostvex.
Neither of these is a knockout argument. But together, they were enough.
What I gave up
Compile times. Go's go build on the same project would take maybe 4 seconds. Rust's cargo build --release with LTO is 2 minutes. Debug builds are 8 seconds, which is OK, but I miss the Go feedback loop.
Goroutines. Tokio's async story is the closest equivalent and it's pretty good, but the mental model is different — every async function colors its callers. You learn to live with it.
Stdlib breadth. Go's stdlib has, broadly speaking, everything I need to write a network service. Rust's stdlib is smaller, and I lean on third-party crates (tokio, serde, quinn, im, blake3, reed-solomon-erasure). Each of those is a maintenance dependency.
Was it worth it
Tentatively yes. The reasons:
- The lock-free merge engine. I don't think I would have been comfortable building this in Go — or at least, I would have spent more time second-guessing the memory ordering. In Rust, the type system enforces the invariants I care about.
- The binary size. Frostvex's release is 3.1 MB. That's small enough to include in a Docker image without thinking, small enough to download over a slow link without complaining.
- Predictable performance. No GC pauses showing up in benchmarks. Tail latencies are tighter than they would be in Go.
Things I miss: Go's standard library, simple tooling, fast iteration. The honest answer is that "Rust over Go" was the right call for this specific project; for the next project I might pick Go again.
If you've shipped a similar tool in both languages and disagree, I'd love to hear about it.