2026年7月8日,Bun的作者Jarred Sumner发表了一篇博文,正式宣告Bun的核心运行时从Zig全面迁移到Rust。这不是一次渐进式重构——64个Claude AI实例在11天内完成了100万行代码的机械翻译,6,778次提交,99.8%的测试通过率。5月14日PR #30412合入主分支,Zig代码从此从Bun的主线消失。
Bun从诞生那天起就以「用Zig写的快速JS运行时」为身份标签。这条标签在2026年7月正式作废。
Zig为什么从资产变成了负债
Jarred Sumner在博文里没有回避这个问题。他列出了一份Bun v1.3.14修复的bug清单:heap-use-after-free在node:zlib中反复出现,node:http2的哈希表rehash在JS回调重入时导致use-after-free,UDPSocket.send()在valueOf()回调期间发生ArrayBuffer分离,crypto.scrypt在分配失败路径上泄露密码和盐缓冲区。这份清单有13项,全是内存安全相关的稳定性问题。
Sumner的原话直接得几乎没有公关修饰:「I was tired of going to sleep worrying about crashes in Bun.」
Zig的设计哲学是信任程序员——没有垃圾回收,没有借用检查器,内存管理完全手动。对一个个人项目而言,这种自由度是资产;对一个月下载2200万次、被Claude Code和OpenCode选为运行时的生产级工具而言,它变成了负债。
这里有一个容易被忽略的工程约束:Bun不是一个纯Zig项目。它的20%代码是C++,内嵌了JavaScriptCore、BoringSSL、SQLite等C/C++库。在这个混合内存模型的边界上——GC管理的内存和手动管理的内存在同一调用栈里交织——Zig的defer关键字需要写在每一个可能需要清理的调用点。漏掉一处是内存泄漏,写重了是double-free。
这种问题Zig社区并非没有意识到。TigerBeetle有TigerStyle风格指南来约束内存管理,但风格指南靠的是代码审查,不是编译器。而Bun的bug清单说明了一件事:在53万行代码的规模上,靠人类审查来保证每一次内存操作的清洁是不现实的。
11天,100万行,64个AI实例
Rust的借用检查和Drop语义是Bun转向的核心驱动力。但真正让改写成为可能的,是Anthropic内部尚未发布的Claude Fable 5模型。
改写过程本身有值得拆解的工程细节。Sumner花了3小时与Claude讨论Zig模式到Rust模式的映射,产出PORTING.md。接着生成LIFETIMES.tsv——对代码库中每一个struct字段的生命周期做了追踪和标注。先用3个.zig文件做试运行验证流程,然后拆成4个worktree、每个worktree中16个Claude实例并行作业。
核心流程是一个循环:1个实现者写代码,2个对抗性审查者找bug,1个修复者应用修改。实现者和审查者在不同的上下文窗口里运行——这种分离不是噱头。Sumner在博文中举了3个真实案例:对抗性审查者发现uv_close是异步的,而代码在match分支末尾把Box drop掉了,留下libuv持有悬垂指针,随后close回调又free了一次。如果同一个Claude既写代码又审代码,它倾向于让自己的代码通过。
这个流程产出了大量commit——峰值时一小时695次提交,一分钟58次。但代价也真实:IOPS不够导致磁盘读写卡死,git stash和git reset互相踩踏,16,000个编译错误等在前面。cycle dependency是难度最大的一类——原来的Zig代码库是单一编译单元,拆成约100个Rust crate需要打破循环依赖。
总计消耗59亿input token、6.9亿output token、720亿缓存token读取,API成本约16.5万美元。Sumner的估计:人工完成同等工作量需要3个熟悉代码库的工程师一年时间,期间无法做功能开发或bug修复。

结果:更快、更小、更省内存
翻完Rust之后Bun拿到了实打实的提升。HTTP吞吐量(Linux x64, Xeon Platinum 8488C)同时跑Bun.serve、node:http、Elysia、Express、Fastify五个server,v1.4.0比v1.3.14提升了2.8%到4.8%。next build快了4.5%,tsc -b —force快了4.7%。
更有说服力的是内存改善。一个60模块的项目在同一进程内调用Bun.build()两千次:v1.3.14每次build泄露约3MB,第2000次时进程内存膨胀到6,745MB;v1.4.0稳定在609MB。Drop语义让文件路径相关的内存泄漏在错误处理路径上被自动清理——Sumner明确提到,之前在Zig中尝试修复同样问题但没有合入,因为缺乏Drop等价物让团队对修改缺乏信心。
二进制体积也同步缩小:Linux减少6.8MB(88MB→70MB),macOS减少5.5MB,Windows减少3.8MB(94MB→76MB),整体缩水约20%。主要原因是以往Zig代码中过度使用了comptime,而Identical Code Folding和ICU按需解压在Rust改写后被一并做了。
Prisma团队的Alexey Orlenko给出了一个第三方背书:「我们在VM暂停并恢复后遇到内存泄漏和连接池不可恢复的问题。Rust改写出现后,我们用相同的故障模式测试了它。它完美应对。」Prisma Compute的公开Beta已经跑在Bun的Rust版本上。
13,000个unsafe块和社区的分裂
数字摆在这里。对比一下:uv(Charlie Marsh写的Python包管理器,纯Rust项目)35万行代码、约165个unsafe块。Bun的Rust版本在68万行代码中有13,044个unsafe块——密度是uv的40倍。Rust社区有人将这种做法称为「C++ with Rust syntax」。
但Sumner没有回避这个问题。他给出了一个明确的数字:只有4%的Rust代码位于unsafe块内——约27,000行。其中78%的unsafe块只有一行长,要么是来自C++的指针,要么是一次C库调用。这些unsafe块的来源是Bun嵌入JavaScriptCore、BoringSSL、uWebSockets这些C/C++库必须付出的代价——交叉语言边界的指针操作天然需要unsafe。
改写引入了19个已知regression,均已修复。大部分来自两门语言中语法相同但语义不同的角落。debug_assert!在release build中整个表达式被擦除,导致React Fast Refresh的热重载图不再更新。bytemuck::cast_slice遇到奇数长度的slice会panic而非静默忽略。Zig编译用了ReleaseFast(去掉了边界检查),而Rust的release build默认保留边界检查——一处BSS_OVERFLOW_BLOCK_SIZE从Zig的2048被错填为64,导致实际项目中intern文件名数量从840万降到27万时就触发panic。
这些bug的粒度说明了一个事:即使AI完成机械翻译,语义细节的校准仍然需要人类工程师逐行排查。
更大的图景:语言本身正在变成可替换的
Bun从Zig翻到Rust的故事在三个层面同时成立。第一个层面是工程决策——当内存安全的维护成本超过迁移成本时,迁移就是合理的。第二个层面是AI辅助开发——11天完成一个人工需要一年的工作量,而且不是「生成式写代码」的噱头,是有对抗性审查、有测试套件验证、有CI平台全绿的结构化流水线。
第三个层面可能被讨论得最少:语言本身正在变成一种可替换的资产。Mitchell Hashimoto在社区讨论中说了一句被广泛引用的话:「Bun已经证明了他们可以在大概一两周内换成任何他们想要的语言。Rust是可抛弃的——有用就用,没用了就可以扔掉。」这不是夸张。Bun拥有超过100万条断言的TypeScript测试套件——它不依赖运行时的实现语言。只要测试全绿,语言可以换。
但这个结论也有反方。Zig社区的政策已经和Bun的路径产生了不可调和的冲突:Zig软件基金会禁止LLM生成的代码贡献,理由是审查时间应该投资在贡献者身上而不是代码上。Bun被迫fork Zig编译器来加入并行语义分析和多codegen单元——这个fork随着Rust改写已经不再需要。Zig失去的是最具知名度的应用案例,而Rust获得的是一个标志性迁移故事。
Bun的未来在Anthropic手里。作为Anthropic在2025年12月收购的资产,Bun的Roadmap优先级正在向Claude Code的运行时路径倾斜。Bun v1.4.0将正式发布Rust版本,目前已在canary通道可用。
参考链接
- Rewriting Bun in Rust — Bun官方博文
- Hacker News讨论 (462pts, 242 comments)
- Bun’s Rust Rewrite: Engineering Reality, Unsafe Blocks, and the AI-Speed Migration
- Bun PR #30412 — 1M-line Zig-to-Rust rewrite hits main
本文的素材来自公开信息和社区讨论。如果你对这个话题有更深入的一手经验,欢迎指出文中的不足。