Bun 深度拆解:当一个 9 万 Star 的 JavaScript 运行时决定「用 Rust 重写自己」——从 Zig 到 Rust 的百万行代码迁徙如何重塑 JS 运行时的未来
引言:一个让 GitHub 服务器宕机的 Pull Request
2026 年 5 月 14 日,Bun 的创始人 Jarred Sumner 提交了一个 Pull Request:#30412。这个 PR 包含了 100 多万行新增代码、6755 个 commit,直接把 GitHub 的页面渲染引擎干崩了——浏览器加载这个 PR 页面时直接卡死。
原因很简单:Bun 用 Rust 重写了整个核心运行时。
这不是一个小打小闹的实验,而是一个拥有 9 万 Star、每周数十万次下载 的主流 JavaScript 运行时,对自己最核心的底层代码进行了推倒重来式的重构。从 Zig 到 Rust,从内存手动管理到所有权系统,从手写 allocator 到编译期安全保证——这次重写不仅改变了 Bun 的技术栈,更深刻地回答了一个问题:当一个系统级项目长大后,如何在保持性能的同时获得工程上的可持续性?
本文将从架构设计、语言选型、工程实践、性能基准四个维度,深度拆解这次史诗级重写的全貌。
第一章:Bun 的前世今生——为什么是 Zig,又为什么离开 Zig
1.1 Bun 的诞生:为速度而生
Bun 诞生于 2022 年,其核心目标只有一个:比 Node.js 快。
为了实现这个目标,Jarred Sumner 选择了一条非主流的技术路线:
| 组件 | Bun | Node.js |
|---|---|---|
| JS 引擎 | JavaScriptCore (Safari/WebKit) | V8 (Chrome) |
| 核心语言 | Zig | C++ |
| Transpiler | 原生 Zig 实现 | Babel / tsc / swc |
| 包管理器 | 内置 | npm / yarn / pnpm |
| 打包器 | 内置 | webpack / esbuild |
| 测试框架 | 内置 | jest / vitest |
选择 Zig 的理由很直接:
- 极致的底层控制:Zig 允许手动管理内存,可以编写自定义 allocator
- 零隐藏控制流:没有异常机制,没有隐式的内存分配
- 编译期计算:comptime 可以在编译期执行任意代码
- C ABI 兼容:可以无缝调用 C 库
在 Bun 的早期阶段,Zig 的这些特性帮助团队快速实现了高性能的核心组件。但随着项目规模从几千行增长到几十万行,Zig 的一些局限性开始显现。
1.2 Zig 的痛点:当项目长大后
Bun 团队在使用 Zig 的过程中遇到了几个核心问题:
内存安全问题的累积
Zig 没有类似 Rust 的所有权系统,内存管理完全依赖程序员的自觉。在小型项目中这不是问题,但在 Bun 这样每天处理数十亿次请求的运行时中,内存 bug 的代价是巨大的。
Jarred Sumner 在公告中提到:「内存问题消耗了开发团队大量时间进行调试和修复。对于一个每天处理数十亿次请求的 JavaScript 运行时来说,这种保障的价值难以估量。」
生态系统的局限
Zig 的生态系统远不如 Rust 成熟。很多需要的底层库(网络、加密、压缩等)要么不存在,要么质量不够。这意味着 Bun 团队需要自己实现大量基础设施,而不是复用社区已有的高质量实现。
招聘和贡献者门槛
Zig 的开发者基数远小于 Rust。对于一个需要持续增长的开源项目来说,语言的生态规模直接影响社区贡献者的数量和质量。
1.3 为什么不选 Rust?
等等,既然 Rust 这么好,为什么 Bun 一开始不选 Rust?
答案很简单:2022 年的 Rust 还不够好用。
当时 Rust 的异步生态(async/await)还处于早期阶段,tokio 虽然可用但编译时间很长,与 C 库的 FFI 绑定也不如 Zig 直接。更重要的是,Zig 的 comptime 元编程能力在当时是 Rust 无法匹敌的——Bun 的很多核心组件依赖于编译期代码生成。
但到了 2026 年,情况完全不同了:
- Rust 的异步生态已经成熟(tokio、async-std)
- 编译时间优化工具(sccache、cargo-nextest)大量涌现
- FFI 绑定工具(bindgen、napi-rs)日益完善
- 社区中「用 Rust 重写 X」已经成为一种成熟的方法论
第二章:重写的架构设计——「换皮不换骨」的哲学
2.1 两个关键不变量
Bun 的 Rust 重写遵循了一个极其重要的设计原则:保持架构不变。
具体来说,这次重写保持了两个关键不变量:
- 相同的架构设计:数据流、模块边界、API 接口完全不变
- 相同的数据结构:内存布局、序列化格式、缓存策略完全不变
这意味着重写不是推倒重来,而是用更安全的语言替换底层实现。就像把一栋房子的地基从木头换成钢筋混凝土——房子的外观和内部布局不变,但安全性和耐久性大幅提升。
2.2 不依赖 async Rust
一个令人意外的决策是:Bun 仍然不依赖 async Rust。
这听起来违反直觉——Rust 的异步生态已经很成熟了,为什么不用?
原因在于 Bun 的架构设计。Bun 的核心是 JavaScriptCore 引擎,所有的异步操作都由 JSC 的事件循环管理。Rust 的 async/await 会在 JSC 事件循环之上引入第二层事件循环,这会导致复杂性急剧增加。
Bun 选择的方案是:用 Rust 的线程池和同步原语来实现底层 I/O,然后将结果桥接到 JSC 的事件循环。这种「同步 Rust + 异步 JSC」的混合模式避免了双重事件循环的问题,同时利用了 Rust 的内存安全保证。
2.3 极少的第三方依赖
Bun 的另一个标志性特征是极少使用第三方库。在 Rust 重写中,这个原则被进一步强化。
查看 Bun 的 Cargo.toml,你会发现依赖列表出奇地短。大部分核心功能都是自己实现的,包括:
- HTTP 解析器
- 文件系统抽象
- 网络 I/O
- 压缩/解压缩
- 加密原语
这种「造轮子」的策略看似低效,但在系统级项目中其实是合理的:
- 减少供应链攻击面
- 完全控制性能特征
- 避免依赖冲突
- 简化调试过程
2.4 模块化的 Rust 实现
Bun 的 Rust 重写采用了高度模块化的架构:
bun/
├── src/bun.js/ # JSC 绑定层
├── src/bun_runtime/ # 运行时核心
├── src/bun_io/ # I/O 抽象
├── src/bun_http/ # HTTP 实现
├── src/bun_fs/ # 文件系统
├── src/bun_net/ # 网络层
├── src/bun_crypto/ # 加密原语
├── src/bun_transpiler/ # TypeScript/JSX 转译
└── src/bun_test/ # 测试框架
每个模块都有清晰的边界和接口定义。这种设计使得重写可以分阶段进行——可以逐个模块替换,而不是一次性重写整个代码库。
第三章:Rust 的价值——编译期安全的工程意义
3.1 所有权系统:从「信任程序员」到「编译器验证」
Rust 的所有权系统是其最核心的创新。在 Zig 中,内存管理依赖程序员的自觉:
// Zig: 手动内存管理
const allocator = std.heap.page_allocator;
const buf = try allocator.alloc(u8, 1024);
defer allocator.free(buf);
// 如果忘记 defer free,就内存泄漏
在 Rust 中,所有权系统在编译期强制执行内存管理:
// Rust: 所有权系统自动管理
let buf = vec![0u8; 1024];
// buf 在离开作用域时自动释放
// 无法在 buf 释放后访问它——编译器会报错
这种差异在大型项目中的影响是巨大的。Bun 的 Rust 重写「修复了多个长期存在的内存泄漏和 flaky 测试问题」——这些问题在 Zig 版本中可能存在了数年之久。
3.2 借用检查器:消除数据竞争
Rust 的借用检查器不仅管理内存生命周期,还防止数据竞争:
// Rust: 编译期防止数据竞争
let mut data = vec![1, 2, 3];
let ref1 = &data; // 不可变借用
let ref2 = &data; // 另一个不可变借用——OK
// let ref3 = &mut data; // 可变借用——编译错误!
// 无法在存在不可变借用时进行可变借用
在 JavaScript 运行时这种高并发场景中,数据竞争是最难调试的 bug 类型之一。Rust 的借用检查器将这类问题从运行时提前到了编译时。
3.3 零成本抽象:安全性不牺牲性能
一个常见的误解是:「Rust 的安全保证会带来性能开销」。
事实上,Rust 的安全检查是零成本抽象——它们在编译期执行,运行时没有额外开销。Bun 的重写结果证明了这一点:「性能测试在各个平台上均达到或超越原有水平」。
更具体地说,Rust 的某些特性反而带来了性能提升:
- 内联优化:编译器可以更激进地内联,因为没有虚函数调用的不确定性
- LLVM 后端:Rust 使用 LLVM,可以利用其先进的优化 passes
- 无 GC 停顿:所有权系统消除了垃圾回收的需求
3.4 二进制体积的缩减
Bun 的 Rust 重写带来了意外的好处:二进制文件体积缩小了 3-8 MB。
这主要归功于:
- Rust 的单态化(monomorphization)避免了运行时的动态分发
- LTO(Link-Time Optimization)在 Rust 生态中更加成熟
- 移除了 Zig 版本中的一些冗余代码
对于一个以「小而快」为卖点的运行时来说,3-8 MB 的体积缩减是显著的改进。
第四章:AI 辅助重写的工程实践
4.1 Claude Code 的角色
这次重写最引人注目的特点之一是:它是用 AI 辅助完成的。
Bun 团队使用 Claude Code(Anthropic 的 AI 编程助手)来辅助代码转换。具体流程是:
- 人工设计架构和接口
- AI 逐模块将 Zig 代码翻译为 Rust
- 人工审查和调整 AI 生成的代码
- 运行测试套件验证正确性
这种「人机协作」的模式使得 6755 个 commit、100 万行代码的重写在相对较短的时间内完成。
4.2 测试驱动的重写
Bun 的重写采用了严格的测试驱动策略。团队维护了一个完整的测试套件,覆盖所有平台:
# 运行完整测试套件
bun test
# 运行特定模块测试
bun test --filter "http"
bun test --filter "fs"
bun test --filter "crypto"
每个模块的重写都必须通过完整的测试套件。Jarred Sumner 透露:「这次重写已经通过了 Bun 原有的完整测试套件,覆盖所有平台。」
99.8% 的测试通过率引发了社区的讨论——有人质疑 AI 生成的代码是否真的安全。但 Bun 团队的回应很务实:测试套件通过不代表没有 bug,但它确保了行为的一致性。
4.3 Canary 发布策略
Bun 没有直接发布 Rust 版本到 stable 频道,而是通过 canary 频道让早期用户帮助发现问题:
# 切换到 canary 频道
bun upgrade --canary
# 切回 stable
bun upgrade --stable
这种渐进式的发布策略是成熟开源项目的标准做法——不为了抢首发而牺牲质量。
第五章:性能基准——Rust 版本到底快了多少?
5.1 启动时间
JavaScript 运行时的启动时间是开发者最关心的指标之一。Bun 一直以来的核心卖点就是「启动快」。
Rust 版本的 Bun 在启动时间上保持了原有水平:
- 冷启动:< 5ms(对比 Node.js 的 20-50ms)
- 热启动:< 1ms(利用操作系统缓存)
值得注意的是,Rust 的所有权系统允许编译器进行更激进的优化,某些场景下启动时间甚至有所改善。
5.2 HTTP 吞吐量
Bun 内置了 HTTP 服务器,这是其与 Node.js 的重要差异之一。
在 HTTP 基准测试中,Rust 版本的 Bun 表现如下:
- 简单响应:~150K req/s(与 Zig 版本持平)
- JSON 序列化:~120K req/s(略有提升,得益于 Rust 的序列化优化)
- 流式响应:与 Zig 版本持平
5.3 文件系统 I/O
文件系统操作是 JavaScript 运行时的另一个关键性能指标:
// Bun 的文件系统 API
const data = await Bun.file("large-file.txt").text();
await Bun.write("output.txt", processedData);
Rust 版本在文件系统 I/O 上的表现:
- 小文件读取:与 Zig 版本持平
- 大文件流式处理:略有提升(得益于 Rust 的零拷贝 I/O)
- 并发文件操作:显著提升(Rust 的线程池实现更高效)
5.4 内存使用
内存使用是 Rust 重写最显著的改进领域:
- 基础内存占用:降低 ~15%
- 长时间运行后的内存增长:大幅降低(消除了多个内存泄漏)
- GC 暂停:仍然为零(Bun 不使用 GC)
第六章:对 JavaScript 生态的影响
6.1 运行时战争的新格局
Bun 的 Rust 重写改变了 JavaScript 运行时的竞争格局:
| 运行时 | 核心语言 | JS 引擎 | 特点 |
|---|---|---|---|
| Node.js | C++ | V8 | 最成熟,生态最大 |
| Deno | Rust | V8 | 安全优先,TypeScript 原生 |
| Bun | Rust (新) | JSC | 极致性能,All-in-One |
| Ant | Rust | 自研 Silver | 9MB 极小体积 |
Bun 的 Rust 重写使其与 Deno 在技术栈上趋同,但在架构理念上保持了差异:Bun 追求的是「All-in-One」的开发体验,而 Deno 追求的是「安全优先」的运行时环境。
6.2 「用 Rust 重写 X」的方法论
Bun 的成功重写为「用 Rust 重写 X」运动提供了一个可参考的方法论:
- 保持架构不变:重写底层实现,不改变上层设计
- 测试驱动:完整的测试套件确保行为一致性
- 渐进式发布:通过 canary 频道收集反馈
- AI 辅助:利用 AI 加速代码翻译,但人工审查不可省略
- 零成本抽象:安全性不应该以性能为代价
6.3 对 Node.js 的启示
Bun 的 Rust 重写也给 Node.js 社区带来了启示。Node.js 的核心是用 C++ 编写的,面临着类似的维护挑战:
- 内存安全问题
- 招聘 C++ 开发者的难度
- 与现代系统编程语言的集成
虽然 Node.js 不太可能进行类似的重写(规模太大,风险太高),但其核心模块(如 libuv)的 Rust 绑定和渐进式迁移可能是未来的方向。
第七章:实战指南——如何迁移到 Rust 版 Bun
7.1 安装和切换
# 安装 Bun(如果还没有)
curl -fsSL https://bun.sh/install | bash
# 切换到 canary 频道(体验 Rust 版本)
bun upgrade --canary
# 验证版本
bun --version
7.2 兼容性验证
Rust 版本的 Bun 保持了向后兼容,但建议在迁移前运行完整的兼容性测试:
# 运行现有项目
bun install
bun test
bun run start
# 检查 Node.js API 兼容性
bun run --bun node your-script.js
7.3 性能对比
在迁移前后进行性能对比是必要的:
# 基准测试脚本
const iterations = 1000000;
// CPU 密集型测试
console.time("CPU");
for (let i = 0; i < iterations; i++) {
JSON.parse(JSON.stringify({ hello: "world" }));
}
console.timeEnd("CPU");
// I/O 密集型测试
console.time("IO");
const files = await Promise.all(
Array.from({ length: 100 }, (_, i) => Bun.file(`test-${i}.txt`).text())
);
console.timeEnd("IO");
7.4 常见问题排查
如果在迁移过程中遇到问题,可以参考以下排查步骤:
# 1. 检查是否是 Rust 版本特有的问题
bun upgrade --stable # 切回 Zig 版本
bun run your-app # 重新运行
# 2. 如果 Zig 版本正常,报告 bug
bun --version # 记录版本号
# 在 GitHub 提交 issue
# 3. 检查内存使用
bun --smol run your-app # 使用更小的内存配置
第八章:总结与展望
8.1 这次重写意味着什么
Bun 的 Rust 重写不仅仅是一次技术栈的切换,它代表了系统级开源项目的一个演进方向:
- 安全性是基础设施:内存安全不再是「可选的」,而是「必须的」
- AI 改变了开发模式:大规模代码迁移变得可行
- 架构比语言更重要:保持架构不变的重写比推倒重来更安全
- 社区信任需要时间:canary 频道是建立信任的正确方式
8.2 Bun 的下一步
Bun 的 Rust 重写只是起点。接下来,团队计划:
- 优化编译时间:Rust 的编译时间一直是痛点,需要持续优化
- 扩展 WASM 支持:利用 Rust 的 WASM 生态提升 WebAssembly 性能
- 增强 AI 集成:内置 LLM 推理能力,让 Bun 成为 AI 应用的首选运行时
- 完善工具链:调试器、性能分析器、IDE 集成等
8.3 给开发者的建议
如果你是 JavaScript 开发者,这次重写对你意味着:
- 现在就可以尝试:
bun upgrade --canary体验 Rust 版本 - 保持关注:stable 版本的发布时间取决于 canary 反馈
- 不需要改变代码:Rust 版本保持向后兼容
- 享受更好的性能:内存使用降低,长时间运行更稳定
8.4 结语
Bun 的 Rust 重写是 2026 年最值得关注的技术事件之一。它证明了:即使是「用 Rust 重写 X」这样看似疯狂的想法,在正确的方法论指导下也是可行的。
对于整个软件行业来说,Bun 的经验提供了一个有价值的参考:当一个项目长大到一定程度时,为未来的稳定性投资——即使这意味着重写核心代码——是值得的。
附录 A:关键数据一览
| 指标 | 数值 |
|---|---|
| 重写 commit 数 | 6755 |
| 新增代码行数 | 100 万+ |
| 二进制体积变化 | 缩小 3-8 MB |
| 性能变化 | 持平或提升 |
| 测试通过率 | 99.8% |
| 内存泄漏修复 | 多个长期存在的 |
| 发布渠道 | canary(暂未 stable) |
附录 B:参考资源
- Bun 官方仓库:https://github.com/oven-sh/bun
- Rust 重写 PR:https://github.com/oven-sh/bun/pull/30412
- Bun 中文文档:http://www.bunjs.cn/
- Jarred Sumner 的公告:Bun Discord / Twitter
- Rust 官方文档:https://doc.rust-lang.org/book/