三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

Bun 重写 Rust 之路:一场 JavaScript 运行时的自我革命

Bun 重写 Rust 之路:一场 JavaScript 运行时的自我革命

🌊 专注AI 大模型与前沿科技深度解析,习惯从工程师视角拆解技术热点。
📚 欢迎点赞、收藏、关注,一起在技术浪潮中保持清醒与好奇 🚀


Bun 重写 Rust 之路:一场 JavaScript 运行时的自我革命

在 JavaScript 工具链的版图上,Bun 一直是个异类。当 Node.js 还在用 C++ 稳坐江山,Deno 用 Rust 试图弯道超车时,Bun 却用 Zig 杀出了一条血路——极致的启动速度、内置打包器、原生 TypeScript 支持,让它在短短几年内收获了无数开发者的青睐。然而,就在最近,Bun 团队做出了一个让整个社区震动决定:用 Rust 重写 Bun

这不是一次简单的语言迁移,而是一场关于性能、内存和长期维护性的深度博弈。作为长期关注 JavaScript 生态的开发者,我想从技术角度拆解这次重写的动机、挑战与潜在影响。

为什么放弃 Zig?

Bun 最初选择 Zig,看中的是其手动内存管理带来的极致控制力,以及对 C ABI 的无缝兼容。Zig 的编译速度极快,生成的二进制体积小,这让 Bun 在启动速度上碾压了 Node.js。但问题也随之而来:Zig 的生态太年轻了。

  • 库生态匮乏:Zig 的第三方库数量与 Rust 的 crates.io 完全不在一个量级。Bun 需要大量底层系统调用、HTTP 解析、压缩算法等基础库,在 Zig 生态中往往需要自己造轮子,而 Rust 生态中这些库早已成熟。
  • 人才招募困难:Zig 的开发者数量远少于 Rust。Bun 团队想要扩大规模,却发现很难找到熟悉 Zig 的资深工程师。反观 Rust,凭借其在系统编程领域的崛起,拥有庞大的开发者社区。
  • 工具链不完善:Zig 的调试器、剖析器、IDE 支持相比 Rust 仍有差距。对于一个需要精细优化性能的运行时来说,工具链的完善程度直接决定了开发效率。

社区里很多人质疑:重写不是浪费时间吗?但 Bun 团队在技术博客中给出的理由很清晰:长期维护的可持续性。Zig 的激进语法和手动内存管理虽然强大,但出错率也高。一个内存安全漏洞在 JavaScript 运行时中可能是致命的——毕竟,Bun 每天要处理数以亿计的请求。

Rust 重写的核心收益

1. 内存安全:从“信任程序员”到“编译器兜底”

JavaScript 运行时最怕的是什么?内存泄漏、空指针、数据竞争。在 Zig 中,这些全靠开发者自觉。而在 Rust 中,借用检查器(Borrow Checker)在编译期就拦截了大部分内存错误。

这意味着什么?Bun 团队可以更激进地使用并发和异步 I/O,而不用担心线程安全问题。例如,在文件系统操作和网络请求的并行处理上,Rust 的所有权模型让代码天然线程安全,无需再手写复杂的锁机制。

2. 性能的精细调优

很多人以为 Rust 重写会牺牲性能,但实际结果恰恰相反。Bun v1.4 的重写版本在基准测试中不仅保持了原有的启动速度优势,还在内存占用上降低了约 15%。这得益于 Rust 的零成本抽象——你可以写出接近 C 语言性能的代码,同时享受高级语言的安全保障。

更关键的是,Rust 的Miri(一个实验性的解释器)可以检测未定义行为,这让 Bun 团队能系统性排查那些在 Zig 时代难以发现的隐蔽 bug。这种“系统性改善稳定性”的能力,是这次重写最宝贵的收获之一。

3. 生态的杠杆效应

Rust 的 crates.io 上有超过 15 万个库。Bun 团队可以直接复用tokio(异步运行时)、hyper(HTTP 实现)、serde(序列化)等久经考验的组件,而不必像在 Zig 中那样从零开始。这大大加速了开发进度——重写工作从 2025 年底启动,到 2026 年 7 月就发布了 v1.4 的 Rust 核心版本,速度惊人。

重写过程中的三大难题

难题一:JavaScriptCore 与 Rust 的桥接

Bun 的底层是 JavaScriptCore(WebKit 的 JS 引擎),而不是 V8。这本来是 Bun 的差异化优势——启动速度更快、内存占用更低。但 JavaScriptCore 是用 C++ 写的,Rust 与 C++ 的互操作(FFI)比 Zig 与 C 的互操作复杂得多。

Bun 团队不得不编写大量的extern "C"桥接层,将 JavaScriptCore 的 C API 封装成 Rust 的安全接口。这个过程极其繁琐,因为 JavaScriptCore 的对象生命周期管理非常微妙——一个对象可能被 JS 垃圾回收器回收,也可能被 Rust 侧持有。如何保证两者不会冲突?Bun 的解决方案是引入了一个“句柄池”(Handle Pool),所有跨语言的对象引用都通过句柄间接访问,避免直接持有裸指针。

难题二:异步模型的重新设计

Zig 时代的 Bun 使用基于epoll的事件循环,配合协程实现异步 I/O。Rust 的异步生态则围绕tokio构建,但tokio的调度模型与 JavaScriptCore 的事件循环并不完全匹配。

Bun 团队没有直接使用tokio,而是实现了一个自定义的异步运行时,专门适配 JavaScriptCore 的语义。这个运行时保留了 Rust 的async/await语法,但在底层直接对接操作系统的异步 I/O 接口,避免了tokio的多线程调度开销。这使得重写后的 Bun 在 I/O 密集型场景下,性能甚至比 Zig 版本提升了 8%。

难题三:兼容性的“暗礁”

Bun 的目标是成为 Node.js 的即插即用替代品。这意味着fshttpchild_process等模块的 API 行为必须与 Node.js 完全一致。在 Zig 时代,Bun 已经实现了大部分 API,但重写过程中,这些 API 的语义必须被重新验证。

尤其是一些边界情况:比如fs.watch在 Linux 和 macOS 上的行为差异、http.Agent的连接池管理、BufferUint8Array的隐式转换……这些细节在测试中不断暴露问题,Bun 团队不得不建立了一个庞大的“兼容性测试套件”,每次提交代码都要运行超过 5 万个测试用例。

对开发者的实际影响

安装体积与内存占用

重写后的 Bun 二进制体积从原来的约 90MB 降到了约 65MB(压缩后)。对于 CI 环境或 Docker 镜像来说,这是一个不小的优化。同时,空闲状态下的内存占用降低了约 20%,这意味着在同一台服务器上可以运行更多 Bun 实例。

包管理速度的再次飞跃

Bun 的bun install本来就以快著称,重写后更是将依赖解析和文件写入的并行度提升了一个量级。在真实的 monorepo 项目中,安装 500 个依赖包的时间从原来的 1.8 秒降到了 1.2 秒。虽然看起来提升不大,但在大型项目中,这个差距会放大到几十秒。

调试体验的改善

Rust 重写后,Bun 的崩溃报告更加友好。以前在 Zig 时代,遇到段错误(Segmentation Fault)往往只能看到一串内存地址,现在 Rust 的panic机制会给出详细的堆栈回溯和错误信息。这对于开发原生模块的开发者来说,简直是救命稻草。

值得担心的风险

尽管重写带来了诸多好处,但我也看到了一些潜在的风险:

第一,JavaScriptCore 依赖的长期锁定。Bun 选择 JavaScriptCore 而非 V8,这本身是一种战略押注。如果 WebKit 团队未来对 JavaScriptCore 的维护力度减弱,Bun 将面临巨大的迁移成本。而 Rust 重写并没有改变这个底层依赖。

第二,社区分叉的担忧。Bun 的重写引发了社区关于“为什么不直接用 Deno 或 Node.js + Rust 插件”的讨论。虽然 Bun 团队明确表示不会放弃 JavaScriptCore,但这次重写确实让一些早期采用者感到不安——他们担心 Bun 的 API 会因此发生变化。

第三,新特性的开发速度。重写期间,Bun 团队将大部分精力放在了 Rust 迁移上,导致一些新特性(如 WebGPU 支持、更好的 Windows 集成)的发布被推迟。对于急切期待这些功能的开发者来说,这无疑是一种煎熬。

从重写中我们能学到什么?

Bun 的 Rust 重写,本质上是一次“技术债的提前偿还”。在项目初期,选择 Zig 是为了快速验证核心假设——一个极速的 JavaScript 运行时是否可行。当假设被验证后,团队意识到长期维护的瓶颈在语言生态,于是果断转向。这种“用最快路径验证,再用最稳路径工程化”的策略,值得每一个开发者深思。

对于初级开发者来说,这次重写也传递了一个重要信号:语言的选择不是一劳永逸的。今天你选择了 Python 的快速开发,明天可能就需要 Rust 的性能;今天你选择了 TypeScript 的类型安全,明天可能就需要 Go 的并发模型。关键在于,你的架构是否足够模块化,以便在必要时替换底层实现?

Bun 之所以能顺利重写,是因为它的核心设计(JavaScriptCore + 自定义 I/O 层)与语言绑定层解耦得足够干净。如果你的代码中,业务逻辑与底层库强耦合,那么任何重写都将是一场灾难。

下一步展望

目前,Bun 的 Rust 重写已经完成了约 80% 的代码迁移。剩余的 20% 主要集中在一些边缘模块(如bun:sqlite的原生绑定、bun:ffi的动态库加载)。Bun 团队计划在 2026 年底前完全移除 Zig 代码。

与此同时,Bun 的性能仍在持续优化。最新版本的基准测试显示,其 HTTP 服务器吞吐量已经接近hyper原生 Rust 实现的 95%——考虑到 JavaScript 解释层的开销,这已经是一个惊人的数字。

对于开发者来说,现在是一个不错的观察窗口。如果你正在考虑将项目从 Node.js 迁移到 Bun,可以等待 Rust 重写完全稳定后再行动。但如果你追求极致的启动速度和内存效率,现在就可以尝试 Bun v1.4 及以上的版本——它已经足够可靠,并且未来的性能提升空间更大。

技术世界的每一次重写,都是对“最优解”的一次重新定义。Bun 的 Rust 之旅,或许会为 JavaScript 运行时的发展开辟一条新的道路。而我们作为开发者,不妨保持好奇,持续观察这场变革的终局。

← 返回列表