On July 15, 2026, Richard Feldman, creator of the Roc programming language, published a technical blog announcing that over 487 days they rewrote 300,000 lines of Rust into Zig. The compiler’s incremental build time dropped from 3.4 seconds to 35 milliseconds — a full 100x faster.
This isn’t an isolated event. Before this, the Gleam language’s compiler walked the same path: from Rust to Zig. On the other side, the Bun runtime completed the reverse move earlier in 2026 — from Zig to Rust, in just 11 days.
Two teams of top-tier compiler developers made opposite choices in opposite directions. When both sides of an argument have real code committed with real money behind it, you know: at bottom this is a balance problem.

What Do 35 Milliseconds Actually Mean?
3.4 seconds is already fast. I’ve written plenty of Rust projects; cargo check taking two or three seconds, rust-analyzer humming in the background — honestly, you get used to it. Rust’s compile speed has improved dramatically over the past 18 months; Rust 1.97 cut incremental builds by two-thirds versus 1.85. Feldman himself praises that effort in the post.
But 35 milliseconds is another species entirely.
35 milliseconds means the instant you hit Ctrl+S — before your finger even lifts off the key — the compiler has already told you the result. That’s the feedback loop vanishing. Zig team member mlugg described the experience on Lobsters with a detail: each incremental build takes him around 30 milliseconds, of which the linker’s work is only about 1 millisecond. Because Zig’s new ELF linker is designed for per-function incremental linking — change one function, the compiler emits new machine code, and the linker overwrites the old code in place within the output file’s .text section. No syscall, because the output file is mmap’d into memory.
This “zero-wait” changes the relationship between developer and compiler. You no longer batch up changes before running a build; instead you probe rapidly between edit and feedback — using a compiled language like an interpreted one. Roc’s new compiler even supports hot code loading: a running server can switch to modified code without restarting the process. That’s standard in the Python world, but a luxury among compiled languages.
So What Does Rust’s Type Safety Actually “Cost”?
If 35 milliseconds is the sweetener Zig offers, how expensive is the “safety tax” Rust charges? Let’s break it down.
Bill one: compile time. Rust’s borrow checker does something extraordinarily luxurious at compile time — it proves your program has no use-after-free, no double-free, no data races. That proof requires traversing the entire program’s reference graph, with complexity growing super-linearly with code size. Rust’s incremental compilation keeps improving, but the borrow checker’s nature means it can never be “instant” the way Zig is.
Bill two: architectural freedom. The Roc compiler makes heavy use of arena allocators and struct-of-arrays layouts — every data structure uses 32-bit indices instead of pointers, split into independent arrays by field. This style flies on modern CPUs and even mmap’s straight to disk for “zero-parse deserialization” — on the second roc check, all already-parsed structures jump from disk into memory at nearly memcpy speed.
But here’s the catch: this programming style almost inevitably fights Rust’s borrow checker. The arena-plus-index pattern routes around Rust’s ownership system, which means your unsafe ratio far exceeds a typical Rust project. Feldman’s team had roughly 1,200 unsafe blocks across 300,000 lines of Rust — over an order of magnitude denser than rustc’s own unsafe density. When unsafe goes from “a few corners needing audit” to “everywhere, as a matter of course,” the sense of safety the borrow checker provides starts to erode.
Both Sides’ Ledgers: Numbers Lie, and Tell the Truth
Feldman did something honest: he tallied the memory-corruption bug counts in each compiler version.
Rust version: 21. Zig version: 10.
At first glance, Zig wins. But break it down — all 21 of the Rust version’s memory-corruption bugs were miscompilations (the compiler emitted wrong machine code); none occurred in the compiler’s own logic. The borrow checker did its job. Of Zig’s 10, 8 were also miscompilations; the remaining 2 were use-after-free — both in the code that renders filenames in error reports, with the symptom that filenames in error messages turned to garbage.
Feldman’s conclusion is strikingly calm: “Looking back at 18 months of development, hundreds of bug reports, hundreds of thousands of lines of code, my main feeling is: it doesn’t matter which you pick.” Those 2 use-after-free’s — Rust’s borrow checker would catch them, Zig’s ReleaseSafe mode would panic at runtime — but all three approaches’ actual impact was “two bug reports: some error messages don’t show filenames.”
That conclusion forms a subtle contrast with the Bun team. When moving from Zig to Rust, Bun stressed that for projects managing both JavaScript GC values and manually-managed memory, use-after-free is “a huge source of bugs.” Feldman fully agrees — then points out that Roc’s compiler doesn’t need to interoperate with JavaScript.
It’s not about who’s right. The key is: context decides everything.

The Community Rift: This Isn’t a Holy War
The post drew 175 points and 62 comments on Lobsters and sparked fierce debate on Hacker News too. But the notable thing is that both sides brought grounded, well-reasoned arguments from the front lines.
Rust core team member Ralf Jung flagged a problem with the “rustc has 40,000 unsafe’s” figure Feldman cited — that number includes occurrences in the standard library, tests, and comments; the actual compiler has far fewer. He also conceded: “I fully agree unsafe Rust is hard to get right, and that’s a worry I take very seriously.”
llogiq — author of the compact_arena crate — noted that Rust’s typed-index system can distinguish indices from different arenas at compile time, avoiding the “wrong array” problem. But he conceded this technique fails when the number of arenas is unknown at compile time.
aapoalas — a self-described “data-oriented design enthusiast” Rust user — voiced the classic conflicted stance: “As a data-oriented design enthusiast and heavy Rust user, it’s sad to see a kindred project leave Rust.” He then listed his own series of attempts to achieve similar optimizations in Rust, with an honest tone of “not wanting to give up.”
What I find healthy about this debate: nobody called the other side idiots. Nobody said “picking Rust means you don’t understand performance” or “picking Zig means you don’t care about safety.” Everyone acknowledged it’s a real trade-off — then made different choices based on their own project’s context.
A Deeper Contest: comptime vs. proc macro
Behind the compile-speed numbers game lies a more telling philosophical split: how do you do compile-time metaprogramming?
Zig’s answer is comptime — you write ordinary Zig code, just marked to run at compile time. It’s like an interpreter built into the compiler, making generics, code generation, and type manipulation as natural as writing runtime code. No second syntax, no token-tree manipulation, no hygiene-macro quirks.
Rust’s answer is proc macros — a separate Rust program that runs at compile time, takes a token stream, manipulates it, outputs a token stream. Extremely powerful (in theory you can do anything), but also extremely heavy. Every proc macro is its own crate, and compiling it takes time. Zig’s comptime is embedded in the same compilation pass, with almost no extra overhead.
That’s a hidden reason Zig compiles fast: it doesn’t need to compile a macro system first, then compile your code. Metaprogramming and the main program share one compiler pipeline. Feldman wrote in the post: “I like that Zig has no macros.” — on its own that sounds like a jab, but in context it expresses a subtractive aesthetic: one fewer mechanism, one fewer layer of abstraction, one less compile burden.
Of course, subtraction means loss. Feldman also admits he misses Rust’s trait system and private fields. Those are expressive powers Rust bought with addition. Picking Zig means accepting that “simplicity” matters more than “expressiveness” — at least for projects where compile speed is a matter of life and death.
My Take: You Can’t Have Both — But You Can Pick the Plate
This isn’t a side-taking piece. My genuine feeling after the analysis above: the Rust-vs-Zig relationship is shifting from “which is better” to “which fits what.”
If your project is like a web server or database — structure relatively stable, unsafe concentrated in a few hot spots, and what you need most is the borrow checker’s long-term confidence — Rust remains the safest choice today.
If your project is like a compiler — code iterated and refactored constantly, unsafe everywhere, and compile speed directly shapes your thinking rhythm — Zig is becoming an option you can’t ignore. Because in that scenario, the price of safety is different.
The Roc team redefined the kind of safety they need. Their memory-safety problems mostly live in the generated machine code, not the compiler itself — and the borrow checker can’t touch the former at all. When the safety bottleneck falls outside what the language’s guarantees cover, paying compile-time cost for it becomes debatable.
The gap between 35 milliseconds and 3.4 seconds is, at bottom, the concretization of two development philosophies: one believes the machine can prove everything at compile time; the other believes the developer can manage everything at runtime. Neither is perfect — but at least now, developers have genuinely different options.
That may be the best news systems programming has had in years.
References:
- Richard Feldman: “How Our Rust-to-Zig Rewrite is Going” (July 15, 2026)
- Lobsters discussion: How Our Rust-to-Zig Rewrite is Going (175 points / 62 comments)
- Bun team: “Why We’re Rewriting Bun from Zig to Rust” (2026)
- Zig official dev log: Incremental Compilation Demo
- Gleam language FAQ: Why Rust for the compiler?