Vaccari's Code

Progress of the Rust to Zig rewrite

7/19/2026

Rewriting a 300,000-line compiler is a Herculean task. Doing it in a year and a half, swapping Rust for Zig, and still achieving feature parity with the original version? That's the recent story behind the Roc compiler, and it's a case study full of valuable lessons for any engineering team.

While the Bun project recently shared the opposite experience – a rewrite from Zig to Rust – the Roc team opted for a different direction, and the results are already starting to show, bringing powerful features and a glimpse into the language's future.

The Rewrite Journey and Feature Parity

Over the past 18 months, the team behind the Roc compiler embarked on the ambitious mission of rewriting its 300,000 lines of Rust code to Zig. The decision was not trivial, and the goal was more than just a direct port; it involved significant architectural changes aimed at unlocking new capabilities for the language.

Recently, an exciting milestone was reached: feature parity with the original compiler. This means the new Zig codebase is now capable of doing everything the Rust version did, paving the way for the 0.1.0 release later this year.

A practical example of this achievement is the updated Rocci Bird game, created by Brendan Hansknecht. The game, with less than a thousand lines of Roc code, now runs with the new compiler. The result is remarkable: the updated source code is more concise, and the command roc build --opt=size generates a WebAssembly (WASM) binary of just 31KB – less than half the size produced by the original compiler. WebAssembly is a binary code format that can be executed in web browsers and other environments, ideal for high-performance applications. Seeing those purple pixels come to life with the new compiler brought a smile to the team's face, validating the hard work.

The success of this endeavor is a testament to collective effort. The team expressed deep gratitude to various contributors, such as Anthony Bullard and Sam Mohr for the new parser, Jared Ramirez for the new type-checker, and Ayaz Hafiz for the lambda set resolution system, among many others who dedicated their time and talent to make this vision a reality. The support from sponsors like rwx, Lambda Class, and NoRedInk was also crucial in supporting the contributors.

The Contrast with Bun and the Reasons Behind the Numbers

It's natural to compare Roc's experience with Bun's, which performed a rewrite from Zig to Rust in a mere 11 days for about 500,000 lines of code. Roc's rewrite, on the other hand, took 487 days – 476 days longer. At first glance, this difference might seem to favor Rust or Zig, but the reality is more complex and instructive.

The disparity in rewrite time is not inherently about the Rust or Zig languages. The Roc team emphasizes that Bun's rewrite was a direct port, meaning a translation of existing code to another language, while maintaining the architecture and functionalities. In contrast, Roc's rewrite was driven by a strategic decision to fundamentally change the compiler. This meant redesigning, reinventing, and implementing a series of new features and approaches that did not exist in the Rust version. The techniques used by Bun, focused on rapid portability, simply would not have worked for Roc's objectives.

This distinction is crucial: comparing the original Rust codebase and the new Zig one is not an "apples to apples" comparison. Roc's rewrite was an investment in a new foundation, designed for the future and to unlock capabilities that would have been difficult or impossible to achieve with the previous architecture.

What the Rewrite Unlocked: Innovative Features

The headache of a massive rewrite is justified by the innovations it enables. The new Roc compiler, built in Zig, introduces features that significantly enhance the development experience and performance:

  1. Hot Code Loading: This is a standard feature in interpreted languages like Python, but rare in high-performance compiled languages like Roc. With the new compiler, you can start a web server with roc server.roc, change its code while it's running, and the next request will already be handled by the new version of the code, without needing to restart the server. This drastically accelerates the development cycle.

  2. Optimized Cross-Compiled Binaries: The command roc build server.roc generates an LLVM-optimized (a set of compiler technologies aimed at generating efficient machine code) and self-contained binary, ready for deployment. Furthermore, Roc now supports cross-compilation, meaning compiling code on one platform to be executed on another. Building a static binary for Alpine Linux, for example, is as simple as roc build --target=x64musl. Most impressively, this command will produce the same output bytes (for the same input source code bytes) on any operating system, a guarantee that not all compilers offer.

  3. Pattern Matching with String Interpolation: A new language feature allows string interpolation directly within pattern matching, as seen in the HTTP request handling logic. This not only makes the code more concise and readable but is also compile-time type-safe and, in the provided example, performs zero heap allocations, which means higher performance and lower memory consumption.

Why This Matters

The story of Roc's rewrite is a powerful reminder that, while painful and time-consuming, code rewrites can be crucial strategic investments. It's not just about swapping one language for another, but about seizing the opportunity to fundamentally rethink and refactor a system's architecture. For builders and technology decision-makers, the lesson is clear: assess whether a rewrite is a port or a chance for innovation. When done with a clear vision and to unlock new capabilities, it can lead to significant gains in efficiency, performance, and developer experience, solidifying the foundation for future growth.


Sources


📬 Enjoyed this? Subscribe to the Vaccari's Code newsletter for the next wave of software & AI trends, straight to your inbox: Subscribe here

← all posts · listen to the episode →