
一个人,11 天,把 53.5 万行 Zig 代码重写成 Rust。
这不是某个周末项目的实验。这是 Bun,一个月下载量 2200 万次的 JavaScript 运行时,Claude Code 和 OpenCode 都跑在它上面。而做这件事的人只有 Jarred Sumner 一个,用的工具是 Anthropic 的 Claude Fable 5。
Bun 在 2025 年 12 月被 Anthropic 收购,所以这事情天然带了一层"自家模型写自家代码"的意味。HN 评论区有人直接说了:这就是 Fable 5 的大型广告。但广告不广告的,数据摆在那里,技术细节也全公开了,值得拆开看。
为什么要从 Zig 搬到 Rust
Jarred 列了一长串 v1.3.14 修的 bug,清一色内存问题:use-after-free、double-free、内存泄漏。根源在 Zig 没有自动内存管理,所有生命周期靠 defer 手动写。Bun 嵌了 JavaScriptCore 这个 GC 引擎,GC 对象和手动管理对象混在一起,每个指针都得问自己"这块内存是 GC 管的还是我管的"。
Zig 的哲学是显式控制流、无隐藏行为,这在系统编程里是优点。但当一个项目的内存模型复杂到需要一篇 31000 字的 C++ 风格指南来约束时,"靠人记得住"就变成了信仰问题。Jarred 原话:"我厌倦了睡前还在担心 Bun 的崩溃。"
Rust 的 Drop trait 和借用检查器把这类问题变成了编译期错误。编译器报错比代码审查靠谱,这是整个重写的底层逻辑。
怎么做的:64 个 Claude 同时跑
方法比结果更值得看。
Jarred 没有用"帮我重写 Bun,别犯错"这种祈祷式 prompt。他把工作拆成了约 50 个动态工作流(dynamic workflow),每个是一个写代码→审查→修复的循环。峰值时 4 个工作流并行,每个跑 16 个 Claude 实例,总共 64 个 Claude 同时开工。
核心设计是"对抗性审查"(adversarial review):写代码的 Claude 和审查代码的 Claude 在不同的上下文窗口里,互相看不到对方的推理过程。审查者只拿到 diff,被要求"假设这段代码是错的"。一个实现者配两个以上审查者,实现者不审查,审查者不实现。
博客里贴了三个被审查抓住的 bug,全都编译通过、看着没问题:
第一个,一个 Box 包装的 Pipe 在 match 分支末尾被 drop,但 libuv 的 close 是异步的,libuv 还拿着裸指针,下一个 tick 才调回调。use-after-free 接着 double-free。修复方式是用 Box::leak 把所有权交出去。
第二个,trunc() 对负数时间戳取整方向不对,导致纳秒部分变成负数,timespec 非法。floor() 才对。
第三个,unwrap_or 的参数是立即求值的,当 first.percentage 是 Some 的时候也会触发 second.percentage.unwrap() 的 panic。换成 unwrap_or_else 传闭包才安全。
这三个 bug 的共性是:语法上从 Zig 到 Rust 看着是对的,语义上差了一层。也正是这类"看着对但不对"的转换,贡献了 19 个已知回归里的大部分。
数字
6502 次提交,100 万行 diff。峰值每分钟 1300 行代码。测试套件跨三个平台共 365 万个 expect 断言,0 个被跳过或删除。
成本:59 亿 uncached input tokens,6.9 亿 output tokens,720 亿 cached input token reads,按 API 定价约 16.5 万美元。
Jarred 估算人工做这件事需要 3 个熟悉代码库的工程师干一年,期间没法做新功能、修 bug、修安全问题。他说"我们永远不会那么做。现实选项是什么都不做,永远修顶上那些 bug。"
结果:更小、更快、更稳
二进制体积缩减约 20%:Linux 从 88MB 降到 70MB,Windows 从 94MB 降到 76MB。HTTP 吞吐提升 2% 到 5%,Bun.serve 从 16.96 万 req/s 到 17.77 万 req/s。next build 快 4.5%。
Prisma 的 Compute 公测直接跑在 Rust 版 Bun 上,他们的工程师说之前遇到的内存泄漏和连接池问题,Rust 版"完美处理了"。Claude Code v2.1.181 起也在用 Rust 版 Bun,启动快了 10%,"几乎没人注意到。无聊是好事。"
争议:13000 个 unsafe
HN 评论区不算太平。有人指出合并到 main 时,代码里有 13000 个 unsafe 关键字,没有 Miri 测试,甚至在 safe Rust 里暴露了未定义行为(GitHub issue #30719)。有人翻出 Jarred 9 天前在 HN 上的发言,当时他还说"不会用 AI 做 RIIR(Rewrite It In Rust)",转头就合并了,被指责 gaslighting。
Jarred 在博客里承认了 4% 的代码在 unsafe 块里,78% 的块只有一行(调 C/C++ 库的指针转换)。他也承认了 19 个回归,逐一列了原因和修复。这种透明度在 AI 生成代码的讨论里算是少见的。
另一条争议线是 Zig 社区。有人说得直接:"一个 naive 的重写修掉了内存泄漏、提升了稳定性、缩小 20%、快了 5%,这对 Zig 不是好事。"也有人为 Zig 辩护,说 Bun 的内存问题根源是混用 GC 和手动管理,不是 Zig 本身的错。
我的看法
这篇博客值得读,不是因为"一个人用 AI 重写了 53 万行代码"这个标题,而是因为它把方法论公开了。对抗性审查、分离上下文窗口、把编译器错误当工作队列,这些是可以复制的。
但 13000 个 unsafe 和 19 个回归也说明了一个现实:LLM 写的代码不能盲目信任,哪怕测试全过。测试覆盖的是已知行为,编译器检查的是类型安全,两者中间的语义鸿沟(trunc vs floor、unwrap_or vs unwrap_or_else、异步 close 的所有权转移)仍然需要人来盯。
Jarred 做了一件很多人想做但不敢做的事,而且把过程完整公开了。不管你怎么看 AI 生成代码,这都是一份值得研究的技术文档。