编程 Bun 深度拆解:从 Zig 到 Rust 的 6755 次 Commit——一个 JavaScript 运行时如何用 AI 重写自己

2026-08-02 22:14:12 +0800 CST views 31

Bun 深度拆解:从 Zig 到 Rust 的 6755 次 Commit——一个 JavaScript 运行时如何用 AI 重写自己

引言:当一个运行时决定背叛自己的母语

2026 年 5 月 11 日,Bun 创始人 Jarred Sumner 在 X 上发了一条推文,语气平静得像在说今天天气不错:

"Bun v1.3.14 将于明日发布。如果我们合并 Rust 重写版本,这将是 Zig 的最后一个版本。"

就这么一句话。四年前,Bun 因为选择了 Zig 而显得特立独行——一个 JavaScript 运行时不用 C++,不用 Rust,偏偏选了一个几乎没人听说过的系统语言。四年后,Zig 版本被它的创造者用一条推文宣告了终结。

这不是一次普通的语言迁移。这是一场由 AI 驱动的、涉及 6755 个 commit、超过 100 万行代码的运行时重写。更荒诞的是,重写 Bun 的正是 Claude——那个运行在 Bun 之上的 AI 编程助手。Claude Code 被 Bun 的内存泄漏拖垮了,然后 Anthropic 让 Claude 去重写 Bun,最后 Bun 再继续支撑 Claude Code。

这个闭环,堪称 2026 年软件工程最魔幻的叙事。

本文将从第一性原理出发,深度拆解这次重写的技术细节:为什么 Zig 的内存管理模型在 Bun 的场景下失灵了?Rust 的所有权系统如何解决了问题?Claude Code 是如何在 6 天内完成 96 万行代码迁移的?以及,当 13,000 个 unsafe 代码块出现在一个号称"内存安全"的项目中时,我们该信任什么?


第一章:Bun 的前世今生——从 esbuild 的 Zig 移植到 JavaScript 运行时巨头

1.1 起点:一行 Zig 代码改变一切

2021 年 4 月 16 日,Jarred Sumner 在 Zig 的 GitHub 上提交了第一个 issue。在此之前,他刚刚在 Hacker News 上看到了 Zig 的单页语言参考文档,被那种对底层控制的极致追求所震撼。

Bun 的第一个版本,实际上是 esbuild 的 JavaScript/TypeScript 转译器从 Go 到 Zig 的逐行移植。Jarred 在奥克兰一间狭小的公寓里,用一年时间,在没有 LLM 辅助的情况下,从零构建了 Bun 的雏形。

这个起点决定了 Bun 的基因:它从第一天起就是一个"scope 疯狂膨胀"的项目。JavaScript、TypeScript、CSS 转译器和压缩器;npm 兼容的包管理器;类 Jest 测试运行器;Node.js 兼容的模块解析;HTTP/1.1 和 WebSocket 客户端;几十个 Node.js API 的重新实现——所有这些,都被塞进了一个二进制文件。

1.2 Zig 的选择:为什么不是 C++,不是 Rust?

Zig 的核心卖点是"没有隐藏控制流"。没有构造函数、没有析构函数、没有运算符重载、没有隐式类型转换。一切清理代码都通过显式的 defererrdefer 关键字来执行。

对于 Bun 这种需要极致性能的项目,Zig 的优势是明显的:

  • 编译速度快:Zig 编译器本身就是用 Zig 写的,自举性好
  • C 互操作零开销:Zig 可以直接调用 C 函数,不需要 FFI 绑定层
  • 可控的内存分配:每个内存分配都可以精确追踪生命周期
  • comptime 编译期执行:可以在编译期运行任意代码,减少运行时开销

Bun 的创始人 Jarred 在官方博客中坦言:

"Zig 让 Bun 成为可能。如果不是 Zig,我不可能在一年内构建出这么多东西。"

1.3 巅峰与隐患:2200 万月下载量背后的 4700 个 Issue

到 2026 年初,Bun 的 CLI 已经达到每月超过 2200 万次下载。Claude Code、OpenCode 等 AI 编程工具选择 Bun 作为运行时。Vercel、Railway、DigitalOcean 等平台提供了一等公民级别的 Bun 支持。

但与此同时,Bun 的 GitHub 上积累了约 4700 个未解决的 issue——而几乎"驱动整个互联网"的 Node.js,同期只有约 1700 个。

波兰数字会员系统公司 Rewardo 的 CTO Wojciech Maj 曾做过一个对比:

"Node.js 承担着全球级别的工作负载,却维持着更小的 backlog;而仍处于早期阶段的 Bun,却已经被问题淹没了。"

更致命的是,这些问题中有一大类反复出现:内存泄漏和 use-after-free 崩溃


第二章:内存之殇——为什么 Zig 的 defer 在 Bun 的场景下不够用

2.1 GC 与手动内存管理的碰撞

这是 Bun 面临的根本性技术矛盾:JavaScript 是一门带垃圾回收的语言,但 Zig 是一门不提供内存管理的系统语言

现代 JavaScript 引擎(如 JavaScriptCore 和 V8)对异常处理和垃圾回收有严格的规则。而 Zig,像 C 一样,不替你管理内存。这意味着 Bun 团队必须同时处理两套内存模型:

  • GC 管理的内存:JavaScript 对象、闭包、Promise 等
  • 手动管理的内存:C/C++ 库的内部状态、网络缓冲区、文件句柄等

Jarred 在官方博客中写道:

"正确处理垃圾回收值和手动管理值的生命周期,一直是 Bun 稳定性问题的主要来源——通常是小的内存泄漏,偶尔是崩溃。每一次内存分配都必须被仔细审查。这些字节在哪里被释放?如何确保它只被释放一次?我们是否正确检查了 JavaScript 异常?这个垃圾回收指针是否对保守栈扫描器可见?这是垃圾回收内存还是手动管理内存?"

2.2 一份触目惊心的 Bug 清单

在 Bun v1.3.14 的发布说明中,官方列出了一份令人不安的 bug 清单。这些不是边缘场景的理论问题,而是真实影响用户的生产级 bug:

use-after-free 类

  • node:zlib 中调用 .reset() 时,如果异步 .write() 仍在 threadpool 上执行,会导致 use-after-free 崩溃
  • node:http2 中可重入 JS 回调(例如在 timeout 监听器中调用 session.request())触发 hashmap 重哈希,导致内部流指针失效
  • UDPSocket.send()sendMany() 中,用户代码在 valueOf()toString() 回调中可能在载荷捕获和实际发送之间分离 ArrayBuffer

内存泄漏类

  • crypto.scrypt 中输出缓冲区分配失败时,回调和受保护的 password/salt 缓冲区永远不会被释放
  • tlsSocket.setSession() 每次调用泄漏一个 SSL_SESSION(约 6.5KB),因为 d2i_SSL_SESSION 后缺少 SSL_SESSION_free
  • fs.watch() 的 watcher 在 .close() 后永远不会被垃圾回收,因为引用计数下溢导致每个 watcher 都被固定为 GC root

其他类

  • CSS 解析器中 background-clip 带浏览器前缀和多层背景时的 double-free 崩溃
  • DuplexUpgradeContext 从未被释放——每次 tls.connect({ socket: duplex }) 都泄漏一个完整对象
  • MessageEvent 中的竞态条件崩溃,GC 标记线程在并发访问期间观察到 m_data 中的 torn variant

2.3 defer 的局限性:当清理代码需要"恰好执行一次"

Zig 的 defer 关键字在离开作用域时执行清理代码。这在大多数场景下足够好,但对于 Bun 这种需要同时管理 GC 和手动内存的场景,问题就来了:

fn processRequest(socket: *TCPSocket) !void {
    const buffer = try allocator.alloc(u8, 4096);
    defer allocator.free(buffer);  // 离开作用域时释放

    const response = try handleRequest(socket, buffer);
    defer response.deinit();  // 离开作用域时清理

    try socket.send(response.data);
    // 如果这里抛出异常,defer 会正确执行
    // 但如果 socket.send 内部又调用了 JS 回调,
    // 回调中可能访问已经被 defer 标记为待释放的 buffer
}

核心问题在于:defer 知道什么时候释放,但不知道"谁还在引用这块内存"。当同一个指针被传递给多个函数,而某些函数在调用后仍然引用该指针时,defer 就无法安全地释放它。

Zig 的典型解决方案是:

  1. Arena 分配器:让分配的生命周期与某个明确的作用域绑定(例如解析器状态不会逃逸调用函数,AST 节点适合用 arena)
  2. 引用计数:手动管理引用计数
  3. "pay really close attention":瞪大眼睛仔细审查

方案 3 在 Bun 这种规模的项目中显然是不可持续的。

2.4 Zig 的"no hidden control flow"哲学 vs Bun 的现实

Zig 的设计理念是"没有隐藏控制流"。这意味着:

  • 没有 C++ 的隐式析构函数 ~Destructor
  • 没有 Rust 的隐式 Drop
  • 清理代码必须在每个调用点显式写出

这种设计在很多项目中是优点——你能精确知道每一行代码在做什么。但对于 Bun 这种需要同时处理 GC 和手动内存的场景,它变成了一个负担。

如果用 Zig 实现类似 Rust 的智能指针,代码会变成这样:

fn foo(a_ptr: SharedPtr(TCPSocket)) !void {
    const a: *TCPSocket = a_ptr.get();
    defer a_ptr.deref();

    const b = try do_something_with_a(a);
    defer b.deref();

    // ...
}

而 Bun 期望的 Zig 代码是这样的:

fn foo(a: *TCPSocket) !void {
    const b = try do_something_with_a(a);
    // ...
}

前者虽然可行,但失去了 Zig 简洁优雅的本色。Jarred 坦言:

"自制的智能指针提供了比 Rust 更差的 ergonomics,却没有任何保证。我不想这么做。"


第三章:为什么是 Rust——从 13,000 个 unsafe 到编译器保证

3.1 Rust 的杀手锏:编译器替你检查内存安全

Rust 的核心价值不在于"没有 unsafe",而在于它让你必须显式标记 unsafe 代码,并通过所有权系统在编译时捕获大部分内存错误

对于 Bun 之前反复出现的 use-after-free、double-free 和"忘记在错误路径释放"等问题,Rust 的安全子集可以直接编译为编译错误:

fn process_request(socket: &mut TCPSocket) -> Result<(), Error> {
    let buffer = vec![0u8; 4096];  // 自动管理内存

    let response = handle_request(socket, &buffer)?;
    // response 在这里有效

    socket.send(&response.data)?;
    // buffer 和 response 在函数结束时自动释放
    // 如果中途 return Err,Rust 也会自动清理
    Ok(())
}

在 Rust 中,你不需要手动写 defer,不需要担心"某个回调还在引用这块内存"。编译器会确保在任何代码路径下,内存都只被释放一次,且在释放后不再被访问

3.2 RAII 与 Drop:自动清理的正确打开方式

Rust 的 Drop trait 提供了 RAII(Resource Acquisition Is Initialization)语义。当一个值离开作用域时,它的 drop 方法会被自动调用:

struct Buffer {
    data: Vec<u8>,
}

impl Drop for Buffer {
    fn drop(&mut self) {
        println!("Buffer with {} bytes freed", self.data.len());
    }
}

fn example() {
    let buf = Buffer { data: vec![0; 4096] };
    // 使用 buf 做各种事情...
    // buf 在这里自动释放,不需要手动 defer
}

这与 Zig 的 defer 的区别在于:Drop 是类型系统的一部分,而不是程序员的纪律。你不会忘记写 Drop,因为 Rust 编译器会要求你处理它。

3.3 所有权系统:从"pay really close attention"到"编译器帮你盯着"

Rust 的所有权系统通过三个规则消除了大部分内存安全问题:

  1. 每个值都有一个所有者
  2. 同一时刻只能有一个所有者
  3. 所有者离开作用域时,值被自动释放

这意味着:

  • 你不能同时有两个可变引用指向同一块内存(消除 data race)
  • 你不能在值被释放后继续使用它(消除 use-after-free)
  • 你不能释放已经被释放的内存(消除 double-free)

对于 Bun 这种需要同时处理 GC 和手动内存的场景,Rust 的所有权系统提供了一个清晰的边界:

// JavaScript 对象由 GC 管理
struct JsValue {
    inner: *mut JSC_JSGlobalContext,  // GC 管理的指针
}

// C/C++ 库的内存由 Rust 的所有权系统管理
struct HttpConnection {
    socket: OwnedFd,  // Rust 管理文件描述符的生命周期
    buffer: Vec<u8>,  // Rust 管理缓冲区的生命周期
}

// 两者的交互通过 unsafe 显式标记
unsafe fn js_value_to_buffer(val: &JsValue) -> &[u8] {
    // 这里的 unsafe 表示"我在做编译器无法验证的事情"
    // 但 unsafe 的范围被严格限制在这个函数内
}

3.4 代价:13,000 个 unsafe

但 Rust 不是银弹。Bun 需要与大量底层 C/C++ 代码交互——JavaScriptCore 引擎、uWebSockets/usockets HTTP 服务器、lshpack/lsquic HTTP/3 库、BoringSSL、SQLite。这些交互都必须通过 unsafe 来实现。

开发者 Theo(t3.gg 创始人)在 X 上抛出了一个让 Jarred 不得不正视的对比:

"uv 包含 35 万行 Rust 代码,以及 73 个 unsafe 调用。Bun Rust 移植版已经有 68.1 万行 Rust 代码,并且有超过 13,000 个 unsafe 调用。"

Jarred 几乎立刻回应:

"今天已经下降了大约 2000。我预计它会稳定在 1 万左右,因为 Bun 的大部分内容都是用 C 和 C++ 编写的,这种情况不会改变。"

这个数字引发了一个关键问题:如果一个"内存安全"的重写包含了 13,000 个 unsafe 代码块,它的安全性提升到底有多大?

答案是:仍然比 Zig 版本安全得多。原因是:

  1. unsafe 的范围被显式标记:在 Zig 中,整个代码库都是"unsafe"的(没有任何编译器检查),而在 Rust 中,unsafe 只出现在与 C/C++ 交互的边界
  2. safe 代码部分仍然受编译器保护:即使有 13,000 个 unsafe,其余的代码仍然受所有权系统保护
  3. unsafe 代码更容易审查:你可以专门审查这 13,000 个 unsafe 块,而不是审查整个代码库

第四章:AI 重写的工程学——Claude Code 如何在 6 天内重写 96 万行代码

4.1 为什么是"全部一次重写"而非"增量迁移"

传统的语言迁移策略是增量式的:逐步将模块从旧语言迁移到新语言,在过渡期保持两个版本并存。但 Jarred 选择了截然不同的路径——一次性全部重写

他在官方博客中解释了原因:

"在我的经验中(从 esbuild 的 Go 到 Zig 的移植),一次性全部重写比增量重写更好。增量重写会添加你希望最终删除的临时代码,而且在短期和中期都会很痛苦。"

这个判断在 Bun 的场景下尤其正确:Bun 的测试套件是用 TypeScript 写的,不依赖运行时的编程语言。这意味着你可以用同一套测试来验证 Rust 版本是否与 Zig 版本行为一致。

4.2 PORTING.md:一份 576 行的迁移指南

在开始重写之前,Bun 团队编写了一份极其详细的 PORTING.md 文档——一份 576 行的 Zig-to-Rust 迁移指南。这份文档将迁移分为两个阶段:

Phase A(机械移植)

  • 逐文件忠实保留 Zig 的逻辑
  • 即使 Rust 代码暂时不能编译也没关系
  • 关键是"语义投影"——把 Zig 的意思翻译成 Rust

Phase B(编译修复)

  • 逐个 crate 解决编译、构建和运行问题
  • 确保测试套件通过

文档还规定了极其具体的规则:

  • 文件命名规则:如何将 .zig 文件映射为 .rs 文件
  • crate 引用规则:如何组织 Rust 的模块结构
  • 禁止使用的库:禁止使用 tokio、rayon、hyper、futures
  • 禁止 async fn:保持与 Zig 版本相同的异步模型
  • unsafe 必须写明 SAFETY 注释:每个 unsafe 块都必须解释为什么这里是安全的
  • 遇到不确定逻辑时留下 TODO:不要让 AI 自行猜测

4.3 50 个动态工作流:AI 写代码的正确打开方式

Jarred 在官方博客中详细描述了他如何使用 Claude Code 来执行这次重写。核心方法论是:

# 伪代码,不是真实代码
task = todo_list.pop()
result = task()
feedback = await Promise.all([review(result), review(result)])
await apply(feedback, result)

他使用了约 50 个动态工作流,在 11 天内持续运行。每个工作流都是一个循环:

  1. 生成移植指南:将 Zig 的模式和类型映射到 Rust 的模式和类型
  2. 机械移植:将每个 .zig 文件转换为 .rs 文件,遵循 PORTING.md 和 LIFETIMES.tsv
  3. 修复编译错误:逐个 crate 修复
  4. 让子命令工作:让 bun testbun build 等命令能正常运行
  5. 让测试通过:让 Bun 的整个测试套件通过
  6. 大型重构和清理:多个大规模的代码清理 pass

Jarred 强调,这不是简单地告诉 Claude "重写 Bun 为 Rust,别出错"然后祈祷它能工作。他花了大量时间监控工作流的输出,手动检查问题和 bug,然后提示 Claude 编辑工作流来修复问题。

4.4 从 4000 个 commit 到 6755 个 commit:时间线

2026 年 5 月初:Bun 仓库出现 claude/phase-a-port 分支,数十万行 AI 生成的 Rust 代码与原始 Zig 实现并排存在。

5 月 7 日:Jarred 发推称 Rust 迁移已经涉及约 4000 次 commit、96 万行代码,当时只剩下 3 个编译错误。Rust 版本已经能显示 help menu,bun runpackage.json scripts 也已经跑起来。

5 月 9 日:Jarred 宣布 Rust 重写版本已经在 Linux x64 glibc 环境下通过了 Bun 既有测试套件的 99.8%。

5 月 11 日:Jarred 发出那条引爆社区的推文:"如果我们合并 Rust 重写版本,这将是 Zig 的最后一个版本。"

5 月 14 日:Bun 正式宣布核心运行时已从 Zig 重写为 Rust,包含 6755 个 commit。

4.5 GitHub 被"干爆":100 万行代码的 PR

这个 PR(#30412)包含超过 100 万行新增代码,直接导致 GitHub 页面无法加载。这可能是 GitHub 历史上最大的单个 PR 之一。

Jarred 在博客中坦言,他最初并不认为 AI 能做到这件事:

"我没有想到技术已经成熟到可以完成这个壮举。但他做到了,现在我正在比喻性地从一个写着'味道就像不再是我的问题了'的杯子里喝着美味的茶。"


第五章:Andrew Kelley 的回应——Zig 创始人的视角

5.1 一段复杂的关系

2026 年 7 月 9 日,Zig 语言创始人 Andrew Kelley 发表了一篇题为"My Thoughts on the Bun Rust Rewrite"的博客文章。这篇文章不仅是一篇技术评论,更是一段关于开源项目之间关系破裂的叙事。

Andrew 将 Jarred 描述为一个具有强烈"初学者能量"的人——行动迅速,大量尝试,但工程产出常常是中等水平。这种特质在学习阶段是健康的,但在领导一个 VC 支持的创业公司时就变成了问题。

5.2 代码质量之争

Andrew 指出,Zig 团队经常检查用户项目的源代码,以了解语言如何影响用户。当他们检查 Bun 的代码库时,看到了令人不安的实践:

"Hacks on top of hacks。滥用断言。最重要的是,鲁莽地冲过一个又一个功能,几乎没有花时间反思和消除 bug 及技术债。Jarred 在能够使用 LLM 之前就已经在写 slop 了。"

他特别提到了 Bun 团队对 Zig 社区严格"no-AI policy"的态度。Zig 基金会成员 Loris Cro 曾公开表示,大量 LLM 贡献只会制造"幻觉 PR"、"垃圾噪音"以及动辄上万行、根本无法维护的提交。

5.3 对 Rust 重写的技术批评

Andrew 对 Bun 官方博客中的技术声明提出了几点质疑:

  1. 性能提升归因于 LTO:Bun 官方博客将性能提升归因于 Link-Time Optimization,但 Andrew 指出 Zig 一直支持 LTO,而且之前因为 LLVM bug 而默认关闭了——这些 bug 同样影响 Rust。

  2. 模糊了"风格指南"和"语言特性"的边界:博客暗示你必须在"风格指南"和编程语言特性之间选择,但 Andrew 认为真正消除 bug 的方式是投入工程资源来修复它们(如 TigerBeetle 所做的),而不是换语言。

  3. 编译速度:Andrew 指出 Zig 编译器项目约 60 万行代码(与重写前的 Bun 规模相当),从零构建只需 16 秒,增量编译只需 90 毫秒。而 Bun 的 Rust 重写版本的编译速度如何?博客中没有提及。

5.4 关系的本质:价值观分歧而非技术之争

Andrew 总结道,Bun 和 Zig 之间的问题与语言特性无关,而与两个项目的价值体系分歧有关:

"Bun 和 Zig 之间的主要问题与 Zig 和 Rust 的语言特性无关,而与两个项目分叉的价值体系以及随之而来的关系破裂有关。"


第六章:技术深潜——Rust 重写后的架构变化

6.1 两个"不变"的关键设计

尽管语言从 Zig 换成了 Rust,Bun 重写后的架构保持了两个关键不变:

  1. 相同的架构设计:Bun 依然是一个"全能型"运行时,包含转译器、包管理器、测试运行器、HTTP 客户端等所有组件
  2. 相同的数据结构:底层的数据结构没有改变,只是用 Rust 重新实现了

这意味着重写不是"推倒重来",而是"在保持原有架构优势的基础上,用更安全的语言替换底层实现"。

6.2 极少的第三方依赖

Bun 依然使用极少的第三方库,依然不依赖 async Rust。这个决定在 Rust 社区中是异类的——大多数 Rust 项目会大量使用 tokio、hyper 等生态库。

Bun 不使用 async Rust 的原因与 Zig 版本相同:性能。异步运行时会引入额外的抽象层和内存分配,而 Bun 需要控制每一个内存分配的时机和方式。

6.3 unsafe 代码的治理策略

面对 13,000 个 unsafe 代码块,Bun 团队采取了以下策略:

  1. 逐步减少 unsafe:Jarred 在推文中表示 unsafe 数量已经从 13,000 下降了约 2,000,并预计会稳定在 10,000 左右
  2. SAFETY 注释:每个 unsafe 块都必须有 SAFETY 注释,解释为什么这里的 unsafe 是安全的
  3. 后续重构:在 Bun v1.4 发布后,团队计划逐步重构代码,使其更像"idiomatic Rust"

6.4 二进制体积的变化

重写后的 Bun 二进制文件体积缩小了 3-8 MB。Jarred 在博客中解释,这些体积优化与语言重写无关——它是团队在重写过程中做的工程工作,而这些工作本应早就在 Zig 代码库中完成。

Andrew Kelley 对此评论道:

"博客中关于'二进制体积'的部分,恰恰说明了为什么博客花了这么长时间才出来——你们在做那些本应在 Zig 代码库中从一开始就做的工程工作。"


第七章:更宏大的叙事——AI 重写软件的大趋势

7.1 不只是 Bun

Bun 的重写不是孤例。类似的 AI 驱动极限重写正在多个领域同时发生:

  • Cloudflare 在一周内借助 AI 重新实现了 Next.js API 的大部分能力
  • Ladybird 浏览器 在两周内将自己的 JavaScript 引擎从 C++ 迁移到了 Rust

7.2 Jarred 的预言

Jarred 自己在 5 月 3 日发过一条推文:

"这种 pipeline,任何 VC 支持的 OSS 或者有大量 GitHub issues 的公司都能搭建。更普遍地说,它可以用于自动修复用户报告的 bug。"

他甚至更早预言过:

"我预计开源软件会走向完全相反的方向——未来甚至可能变成'禁止人类贡献代码'。人类依然会负责讨论问题、决定优先级,但真正写代码、提交 PR、回复和处理反馈、完成实现的工作,最终都会由 LLM 来完成。"

7.3 "Vibecoded Disaster" 之争

Bun 的重写引发了关于 AI 生成代码质量的激烈讨论。开发者社区的核心担忧是:

流程问题:uv 的 Rust 代码是由人类开发人员编写的,每一行都经过了审查。而 Bun 的 Rust 代码由 AI 编写、AI 审核、AI 批准和合并。

安全问题:6 天、96 万行、AI 生成、AI 测试,最后带着 1 万个 unsafe 直接合并——这种流程是否足够安全?

Jarred 回应了"被迫重写"的猜测:

"没人逼我这么做。"

但社区的担忧并未消散。

7.4 信任问题

当你的 CTO 说"我们要把代码库从 X 语言重写成 Y 语言"时,他不会再问"需要几个月",而是会问"Claude 需要几天"。

但速度上天的时代,信任只能自己想办法落地。


第八章:性能对比——Zig 版 vs Rust 版

8.1 官方数据

Bun 官方博客声称,Rust 版本的性能测试在各个平台上"达到或超越"了原有水平。性能提升主要归因于:

  1. Link-Time Optimization (LTO):Rust 的 LTO 支持更成熟
  2. 内存泄漏修复:修复了多个长期存在的内存泄漏,减少了 GC 压力
  3. 二进制体积优化:更小的二进制意味着更好的缓存命中率

8.2 编译速度的争议

Andrew Kelley 指出了一个被 Bun 官方博客忽略的关键指标:编译速度

Zig 编译器项目约 60 万行代码:

  • 从零构建:16 秒
  • 增量编译:90 毫秒

Bun 的 Rust 重写版本约 68 万行代码:

  • 编译速度未知(博客中未提及)

这个对比暗示了一个潜在的问题:如果 Bun 的编译速度显著慢于 Zig 版本,开发者体验可能会下降

8.3 内存占用的改善

最显著的改善来自内存占用。之前 Claude Code 的主进程 RSS 内存在约 3 小时的会话中从约 1.7GB 增长到 14GB 以上——泄漏位于 Bun 运行时的 WebKit Malloc 分配器中。

修复后的版本应该能显著改善这个问题,因为 Rust 的所有权系统在编译时就能防止大部分内存泄漏。


第九章:对开发者的影响——你该怎么做

9.1 如果你是 Bun 用户

  1. 不要急于升级到 Rust 版本:Jarred 本人表示,优化工作仍在进行中,最终版本发布前还会有清理工作
  2. 使用 canary 频道测试:通过 bun upgrade --canary 可以提前测试 Rust 版本
  3. 关注稳定性:Rust 版本已经通过了 99.8% 的测试套件,但仍有 0.2% 的差异需要关注

9.2 如果你在评估运行时

Bun 的重写提出了一个重要的评估维度:当你选择一个运行时时,你选择的不只是它的当前状态,还有它的技术债务

Bun 的 4700 个未解决 issue 是一个警示信号。即使 Rust 重写解决了内存安全问题,其他问题(如 Node.js API 兼容性、边缘场景的稳定性等)仍然需要时间来解决。

9.3 如果你在考虑 AI 辅助重写

Bun 的案例提供了一个有价值的参考:

可行的方面

  • AI 能够以人类无法企及的速度完成跨语言迁移(6 天 vs 3 周)
  • 语言无关的测试套件是验证重写正确性的关键
  • 详细的迁移指南(PORTING.md)是成功的基础

需要警惕的方面

  • unsafe 代码的审查不能完全交给 AI
  • 代码质量需要人类的判断和品味
  • "通过测试"不等于"生产就绪"

第十章:总结与展望——Bun 的未来与 JavaScript 生态的变局

10.1 Bun 的未来路径

Bun 的 Rust 重写开启了一个新的篇章。未来的路线图可能包括:

  1. 减少 unsafe 代码:逐步将 unsafe 代码重写为 safe Rust
  2. 引入 Rust 异步生态:虽然当前不使用 async Rust,但未来可能会逐步引入
  3. 性能优化:利用 Rust 的 zero-cost abstraction 进一步优化性能
  4. 社区重建:从 Zig 社区的"前明星"转变为 Rust 社区的新成员

10.2 JavaScript 生态的三足鼎立

Bun 的重写改变了 JavaScript 运行时的竞争格局:

  • Node.js:C++ + V8,最成熟、最稳定的运行时
  • Deno:Rust + V8,注重安全性和 TypeScript 支持
  • Bun:Rust + JavaScriptCore,注重性能和"全能型"体验

三者的技术栈差异正在缩小——Deno 和 Bun 都使用 Rust,Node.js 也在逐步引入 Rust 组件(如新的测试运行器)。未来的竞争将更多地围绕生态、工具链和开发者体验展开。

10.3 AI 驱动的软件工程新范式

Bun 的案例标志着一个新时代的到来:AI 不再只是辅助编程的工具,而是能够独立完成大规模工程任务的参与者

但这也带来了新的挑战:

  • 如何确保 AI 生成代码的质量?
  • 如何建立对 AI 生成代码的信任?
  • 人类开发者在这个新范式中的角色是什么?

这些问题没有简单的答案。但有一点是确定的:速度上天的时代,信任只能自己想办法落地


参考资料

  1. Jarred Sumner, "Rewriting Bun in Rust", Bun Blog, July 8, 2026
  2. Andrew Kelley, "My Thoughts on the Bun Rust Rewrite", July 9, 2026
  3. Bun GitHub PR #30412, "Rewrite Bun in Rust"
  4. Bun v1.3.14 Release Notes
  5. Claude Code Issue #33453, "Memory leak in main process"
  6. GitHub Trending Repositories, August 2026

本文从第一性原理出发,深度拆解了 Bun 从 Zig 到 Rust 的重写过程。技术选型没有绝对的对错,只有适合不适合。Bun 的选择,折射出的是整个软件工程行业在 AI 时代面临的根本性问题:当机器能够以人类无法企及的速度完成重写时,我们该如何定义"质量"和"信任"?

推荐文章

php内置函数除法取整和取余数
2024-11-19 10:11:51 +0800 CST
Python设计模式之工厂模式详解
2024-11-19 09:36:23 +0800 CST
Vue 3 中的 Fragments 是什么?
2024-11-17 17:05:46 +0800 CST
Nginx 反向代理
2024-11-19 08:02:10 +0800 CST
Go 协程上下文切换的代价
2024-11-19 09:32:28 +0800 CST
markdown语法
2024-11-18 18:38:43 +0800 CST
程序员茄子在线接单