Roc's Rust-to-Zig Rewrite: 487 Days, 300K Lines, and What the Numbers Actually Show

TL;DR
Richard Feldman's team rewrote the Roc compiler from Rust to Zig in 487 days. The memory safety numbers challenge assumptions, and the 35ms incremental rebuilds are real.
Richard Feldman just published a detailed post-mortem on rewriting the Roc programming language compiler from Rust to Zig. The rewrite took 487 days and covered roughly 300,000 lines of code. The headline numbers: 35ms incremental rebuilds in Zig vs 3.4 seconds in Rust, and - counter to what you might expect - fewer memory corruption bugs in the Zig version.
Why Rewrite at All?#
The Roc team hit architectural problems that made incremental fixes impractical. Specifically, they struggled with implementing "lambda set resolution" - a system that enables closure captures without heap allocations. As Feldman explained: "the root of our problems was architectural across several compiler phases, and fixing it would require rewriting most of the compiler."
Multiple contributors were already planning partial rewrites. The team decided that if they were going to rewrite anyway, they should evaluate whether Rust was still the right choice.
Why Zig Over Rust?#
The team evaluated four factors:
Build times: This was the killer feature. Zig's -fincremental flag rebuilds a 450K+ line codebase in approximately 35 milliseconds. Rust 1.97.0's incremental builds on the same codebase take 3.4 seconds - about 100x slower.
Memory control: Zig's ecosystem assumes fine-grained allocators and struct-of-arrays layouts throughout. Rust's ecosystem largely assumes a single global allocator. Roc uses "a variety of different memory allocators throughout compilation," making Zig's approach a better fit.
Ecosystem relevance: The Zig compiler contains LLVM bitcode serialization code that Roc could reuse directly. No equivalent was available in the Rust ecosystem.
Unsafe code support: The original Rust compiler had about 1,200 uses of unsafe. Zig's additional safety checks for index-based memory access seemed more helpful for their use case than Rust's borrow checker.
The Memory Safety Numbers#
This is where it gets interesting. Conventional wisdom says Rust's borrow checker should catch more bugs than Zig's manual memory management. Here's what actually happened:
| Category | Rust Compiler | Zig Compiler |
|---|---|---|
| Memory corruption bugs | 21 | 10 |
| Total bugs reported | 2,596 | 431 |
Wait - how did the unsafe language have fewer memory bugs?
Context matters. The 21 Rust bugs weren't from unsafe code blocks in the compiler itself. They were miscompilations - bugs in the generated machine code that caused memory corruption when the compiled program ran. That's a fundamentally different category than memory unsafety in the compiler process.
The 2 memory-related bugs in the Zig compiler were use-after-free issues in error reporting code. Both would have been caught by Rust's borrow checker. But both were also minor in impact - they caused malformed error messages, not security vulnerabilities.
Feldman's conclusion: "after 18 months of development, hundreds of total bug reports, and hundreds of thousands of lines of code... picking a different row would have made no appreciable difference to the project."
What HN is Saying#
The Hacker News discussion generated 92+ comments focused heavily on the technical details.
Pushback on the "unsafe compilers" framing: Steve Klabnik (Rust core team, author of The Rust Programming Language book) questioned Feldman's claim that "for compilers which emit machine code, doing memory-unsafe things is a big part of the job." His point: emitting machine code isn't unsafe - you're just writing bytes. It's executing that code that introduces unsafety. A compiler can absolutely be fully safe Rust while producing unsafe binaries.
The scheduling argument: One commenter claimed Go's runtime scheduler is "literally the most sophisticated scheduling engine in the world" and that Go can outperform Rust on throughput despite theoretical disadvantages. This generated significant pushback, with others noting that Erlang, JVM, and CLR runtimes have comparable or better schedulers.
Adding a borrow checker to Zig: Multiple commenters discussed whether Zig could gain Rust-like safety guarantees. The consensus: it's theoretically possible but would require fundamental language changes. Zig's lack of private fields, for example, makes encapsulating unsafe code impossible.
Rust build times improving: A commenter linked to Rust's 2026 roadmap for fast builds. Many of the goals are targeted for this year, suggesting the gap may narrow.
The Build Time Details#
| Version | Lines of Code | Cold Build | Incremental |
|---|---|---|---|
| Rust 1.85.0 | 354K | 32.4s | 10.0s |
| Rust 1.97.0 | 354K | 25.4s | 3.4s |
| Zig 0.16.0 (at parity) | 320K | 39.6s | 8.6s |
| Zig 0.17.0 (current) | 464K | 32.1s | 0.035s |
The 35ms incremental rebuild in Zig 0.17.0 is dramatic. However, this depends on -fincremental support which still has bugs preventing stable release. The 8.6s incremental time in Zig 0.16.0 is more representative of what's available today.
Rust's 1.97.0 numbers show the language is improving - incremental builds dropped from 10s to 3.4s between versions. But the gap to Zig's target numbers remains large.
Technical Innovation: Zero-Parse Deserialization#
The Roc team implemented what Feldman calls "programming without pointers" - using 32-bit array indices instead of pointers throughout the compiler. This enables a clever optimization: cached compiler data structures can be loaded directly from disk without parsing, matching memcpy speeds when data is in the OS cache.
This technique is common in game programming and is used by Zig's own compiler. It eliminates serialization/deserialization overhead entirely for frequently accessed data.
What the Team Misses from Rust#
- Automatic memory management in tests (requiring explicit
deferstatements in Zig) - Parametric and ad-hoc polymorphism
- Private struct field enforcement
- Dead code detection
- Backward compatibility guarantees between releases
What They Like About Zig#
- No macros (simplifies debugging and code navigation)
- Fine-grained data layout control including non-power-of-2 integer types (u7, u23, etc.)
- Packed structs and inline function options
- Superior build toolchain
- Error handling with natural accumulation
- Allocator-based ecosystem design throughout
The Bigger Picture#
This rewrite challenges the binary "safe vs fast" framing that often dominates language discussions. A few observations:
Memory safety guarantees don't prevent miscompilation bugs. Most of Roc's memory corruption bugs weren't from unsafe compiler code - they were from the compiler generating incorrect output. The borrow checker doesn't help with that.
Ecosystem assumptions matter. Zig's allocator-everywhere pattern was a better fit for Roc's architecture than Rust's global-allocator-by-default. Sometimes the language that's theoretically "safer" isn't the language that helps you write better code in practice.
Incremental compilation is a productivity multiplier. 35ms rebuilds vs 3.4s rebuilds is the difference between flow state and context switching. That developer experience improvement may matter more than theoretical safety properties for a compiler project.
Roc is targeting a 0.1.0 release later in 2026. The codebase has grown to approximately 464,000 lines of Zig code. Whether the rewrite ultimately pays off will depend on Roc's adoption - but the data on the rewrite itself is now public and detailed enough to inform other teams making similar decisions.
Continue Reading#
- A Free Compilers Textbook That Actually Teaches You to Build One
- DeepSeek-TUI: The Rust Terminal Coding Agent With MCP, Skills, and 1M-Token Context
- Goose: The Open Source AI Agent With 70+ MCP Extensions
- Mitchell Hashimoto on Building Ghostty in Zig: Simplicity, Control, and Terminal Performance
- Project Valhalla Arrives: Value Classes Ship in JDK 28 After a Decade of Work
- Zig Creator on the Bun-to-Rust Rewrite: What the Controversy Reveals
- Elm's Road to 1.0: Faster Builds and the Acadia Future
Sources#
Get the next deep dive like this in your inbox
One email a week on News and the rest of the AI dev stack. Free.
Read next
Zig's Incremental Compilation: 50ms Rebuilds From a Core Team Deep Dive
Zig core team member mlugg published the definitive deep-dive on how Zig's incremental compilation works - file pipeline, semantic analysis, dependency tracking, and a custom incremental linker. 244 HN points and a rare steveklabnik endorsement.
8 min readZig Creator on the Bun-to-Rust Rewrite: What the Controversy Reveals
Andrew Kelley's blunt response to Anthropic's AI-assisted Bun rewrite sparked debate about AI marketing, language choices, and what makes engineering decisions honest.
7 min readBun Rewrites 535K Lines of Zig to Rust in 11 Days Using Claude
The Bun runtime completed an AI-assisted rewrite from Zig to Rust, fixing memory safety issues and improving performance. Here is what HN thinks and why it matters for LLM-assisted code migration.
6 min readNew here? Start with
Technical content at the intersection of AI and development. Building with AI agents, Claude Code, and modern dev tools - then showing you exactly how it works.





