Bun Drops Zig for Rust in AI-Assisted Rewrite

Bun, the JavaScript runtime that has gained 22 million monthly downloads, has been rewritten from Zig to Rust. The rewrite, completed in 11 days using Anthropic's Claude Code, replaces 535,496 lines of Zig with Rust. The goal: eliminate the memory safety bugs that have plagued Bun since its inception.

Bun's creator, who wrote the initial version in a year in 2021, described a litany of bugs fixed in v1.3.14 alone: heap-use-after-free in node:zlib, double-free in the CSS parser, memory leaks in crypto.scrypt, and race conditions in MessageEvent. The team had already implemented Address Sanitizer (ASAN) in the Zig compiler, run fuzz testing with Fuzzilli, and shipped ReleaseSafe builds on Windows. But the fundamental issue remained: mixing JavaScript's garbage-collected memory with Zig's manual memory management.

"Zig does not have constructors/destructors, and most cleanup is expected to be written out explicitly at each call site with defer," the author wrote. "For Bun, correctly handling the lifetimes of garbage-collected values and manually-managed values has been a major source of stability issues."

Why Rust Won Over C++ and Smart Pointers

About 20% of Bun's code was already in C++ (JavaScriptCore, uWebSockets, BoringSSL). C++ offers destructors, but the team judged that "we would still be reliant on style guides enforced through code review" to prevent memory bugs. Homegrown smart pointers in Zig were considered but deemed too ergonomically poor.

Rust's borrow checker and Drop trait turned compiler errors into a feedback loop that catches use-after-free and double-free at compile time. "Compiler errors are a better feedback loop than a style guide," the author noted.

The AI-Assisted Porting Process

The rewrite used Claude Code in 50 dynamic workflows running continuously over 11 days. Each workflow was a loop: generate a porting guide mapping Zig patterns to Rust, mechanically port every .zig file to .rs, fix compiler errors, get subcommands like bun test working, and then run the entire test suite.

Key tactics:

  • All-at-once rewrite, not incremental. The author's experience porting esbuild from Go to Zig showed that incremental rewrites add temporary code that becomes painful.
  • Mechanical port first, idiomatic Rust later. The initial Rust code mirrors Zig's architecture. Refactoring to reduce unsafe usage happens after v1.4 ships.
  • No behavioral changes. The exact same TypeScript test suite was used, which doesn't depend on the runtime's language.

Technical Details of the Migration

The porting guide (PORTING.md) mapped Zig types to Rust types. For example, Zig's std.ArrayList(T) became Vec, *T became &T or Box depending on ownership, and Zig's defer was replaced with Rust's Drop or explicit scope-based cleanup.

Lifetimes were tracked in a LIFETIMES.tsv file to help Claude understand how references flow through the codebase. The AI then generated Rust code that compiled with minimal unsafe blocks initially.

One concrete example from the source: Zig code like:

fn foo(a: *TCPSocket) !void {
    const b = try do_something_with_a(a);
    // ...
}

became Rust code with explicit lifetime annotations and ownership transfers, though the exact generated code wasn't shown.

Verification and Confidence

To build confidence in the rewrite, the team relied on Bun's existing test suite—written in TypeScript—which runs on every commit. The author reported that "a high % of the test suite started passing" within days, and the new Rust code "matched up with the original Zig codebase."

Bun's creator manually monitored workflows, reading outputs to check for bugs and prompting Claude to adjust the loops. The rewrite is now merged, and Bun v1.4 will ship on Rust.

What This Means for Developers

If you use Bun (or are considering it), the Rust rewrite promises fewer crashes and memory leaks. The team claims the same architecture, performance, and feature set. The test suite passes, so existing Bun applications should work unchanged.

For other projects considering a language migration, this demonstrates that AI can assist with large-scale rewrites—but only with careful orchestration. The author spent 11 days actively monitoring and correcting the AI's output, not just hitting "generate."

The full blog post with more details is available at the source URL.