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

TL;DR
Roc's Rust-to-Zig compiler rewrite took 487 days. What the memory safety numbers, 35ms incremental rebuilds and the HN reaction actually show.
Last updated: July 16, 2026
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.
FAQ#
Why did the Roc team rewrite the compiler from Rust to Zig?#
The team hit architectural problems across several compiler phases, especially around lambda set resolution, and fixing them meant rewriting most of the compiler anyway. Once a rewrite was on the table, they evaluated build times, memory control, ecosystem relevance and unsafe code support, and Zig came out ahead for their use case.
How much faster are Zig incremental builds for the Roc compiler?#
With Zig 0.17.0 and the -fincremental flag, incremental rebuilds take about 35 milliseconds, compared with 3.4 seconds on Rust 1.97.0. That flag still has bugs preventing a stable release, so the 8.6 second incremental time on Zig 0.16.0 is more representative of what is available today.
Did the Zig version of the Roc compiler have more memory bugs than the Rust version?#
No. The Rust compiler saw 21 memory corruption bugs against 10 for the Zig compiler. The Rust bugs were mostly miscompilations - incorrect generated machine code - rather than unsafe code in the compiler itself, which is a category the borrow checker does not help with.
How long did the Roc rewrite take and how big is the codebase?#
The rewrite took 487 days and covered roughly 300,000 lines of code. The Zig codebase has since grown to approximately 464,000 lines, and Roc is targeting a 0.1.0 release later in 2026.
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 Creator on the Bun-to-Rust Rewrite: What the Controversy Reveals
Zig creator Andrew Kelley on Anthropic's AI-assisted Bun-to-Rust rewrite: the core arguments, the HN debate, and what it says about AI marketing claims.
7 min readZig's Incremental Compilation: 50ms Rebuilds From a Core Team Deep Dive
Zig incremental compilation explained from a core team deep dive: file pipeline, semantic analysis, dependency tracking, the custom linker and HN reaction.
8 min readpgrust Passes 100% of Postgres Regression Tests: What the Rust Rewrite Actually Means
pgrust, a Rust rewrite of PostgreSQL, passes all 46,000+ regression test queries. What it delivers, what it does not, and the HN debate on AI-assisted rewrites.
8 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.





