编程 Bun 深度拆解:当一个 9 万 Star 的 JavaScript 运行时决定用 Rust 重写自己——从 Zig 到 Rust 的百万行代码迁徙如何重塑 JS 运行时的未来

2026-08-03 19:42:24 +0800 CST views 9

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 选择了一条非主流的技术路线:

组件BunNode.js
JS 引擎JavaScriptCore (Safari/WebKit)V8 (Chrome)
核心语言ZigC++
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 重写遵循了一个极其重要的设计原则:保持架构不变

具体来说,这次重写保持了两个关键不变量:

  1. 相同的架构设计:数据流、模块边界、API 接口完全不变
  2. 相同的数据结构:内存布局、序列化格式、缓存策略完全不变

这意味着重写不是推倒重来,而是用更安全的语言替换底层实现。就像把一栋房子的地基从木头换成钢筋混凝土——房子的外观和内部布局不变,但安全性和耐久性大幅提升。

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 编程助手)来辅助代码转换。具体流程是:

  1. 人工设计架构和接口
  2. AI 逐模块将 Zig 代码翻译为 Rust
  3. 人工审查和调整 AI 生成的代码
  4. 运行测试套件验证正确性

这种「人机协作」的模式使得 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.jsC++V8最成熟,生态最大
DenoRustV8安全优先,TypeScript 原生
BunRust (新)JSC极致性能,All-in-One
AntRust自研 Silver9MB 极小体积

Bun 的 Rust 重写使其与 Deno 在技术栈上趋同,但在架构理念上保持了差异:Bun 追求的是「All-in-One」的开发体验,而 Deno 追求的是「安全优先」的运行时环境。

6.2 「用 Rust 重写 X」的方法论

Bun 的成功重写为「用 Rust 重写 X」运动提供了一个可参考的方法论:

  1. 保持架构不变:重写底层实现,不改变上层设计
  2. 测试驱动:完整的测试套件确保行为一致性
  3. 渐进式发布:通过 canary 频道收集反馈
  4. AI 辅助:利用 AI 加速代码翻译,但人工审查不可省略
  5. 零成本抽象:安全性不应该以性能为代价

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 重写不仅仅是一次技术栈的切换,它代表了系统级开源项目的一个演进方向:

  1. 安全性是基础设施:内存安全不再是「可选的」,而是「必须的」
  2. AI 改变了开发模式:大规模代码迁移变得可行
  3. 架构比语言更重要:保持架构不变的重写比推倒重来更安全
  4. 社区信任需要时间:canary 频道是建立信任的正确方式

8.2 Bun 的下一步

Bun 的 Rust 重写只是起点。接下来,团队计划:

  • 优化编译时间:Rust 的编译时间一直是痛点,需要持续优化
  • 扩展 WASM 支持:利用 Rust 的 WASM 生态提升 WebAssembly 性能
  • 增强 AI 集成:内置 LLM 推理能力,让 Bun 成为 AI 应用的首选运行时
  • 完善工具链:调试器、性能分析器、IDE 集成等

8.3 给开发者的建议

如果你是 JavaScript 开发者,这次重写对你意味着:

  1. 现在就可以尝试bun upgrade --canary 体验 Rust 版本
  2. 保持关注:stable 版本的发布时间取决于 canary 反馈
  3. 不需要改变代码:Rust 版本保持向后兼容
  4. 享受更好的性能:内存使用降低,长时间运行更稳定

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/

推荐文章

一个简单的打字机效果的实现
2024-11-19 04:47:27 +0800 CST
Golang 中应该知道的 defer 知识
2024-11-18 13:18:56 +0800 CST
关于 `nohup` 和 `&` 的使用说明
2024-11-19 08:49:44 +0800 CST
H5端向App端通信(Uniapp 必会)
2025-02-20 10:32:26 +0800 CST
GROMACS:一个美轮美奂的C++库
2024-11-18 19:43:29 +0800 CST
JavaScript 上传文件的几种方式
2024-11-18 21:11:59 +0800 CST
CSS 中的 `scrollbar-width` 属性
2024-11-19 01:32:55 +0800 CST
程序员出海搞钱工具库
2024-11-18 22:16:19 +0800 CST
如何在Vue3中定义一个组件?
2024-11-17 04:15:09 +0800 CST
程序员茄子在线接单