编程 Bun 深度拆解:当 JavaScript 运行时决定「用 Rust 重写自己」——从 Zig 到 Rust 的 535K 行代码迁移,一个被 Anthropic 收购的 2200 万月下载量项目如何用 AI 重写 AI 基础设施

2026-08-05 14:16:42 +0800 CST views 6

Bun 深度拆解:当 JavaScript 运行时决定「用 Rust 重写自己」——从 Zig 到 Rust 的 535K 行代码迁移,一个被 Anthropic 收购的 2200 万月下载量项目如何用 AI 重写 AI 基础设施

引言:一个运行时的双重巨变

2025 年 12 月 2 日,JavaScript 世界发生了一件大事:Bun 被 Anthropic 收购。2026 年 7 月 8 日,又一件大事发生:Bun 宣布从 Zig 重写为 Rust。

这两件事单独看都足够震撼,但放在一起看,它揭示了一个更深层的趋势——AI 公司正在重新定义基础设施的构建方式

Bun 不是一个小项目。它有超过 2200 万月下载量,535,496 行 Zig 代码(不含注释),被 Claude Code、OpenCode 等主流 AI 编程工具选为运行时。当这样一个项目决定用另一种语言重写自己,并且是用 AI 辅助完成的——这不仅仅是一次技术选型的变更,而是整个软件工程范式的转折点。

本文将深度拆解 Bun 的这次「自我革命」:从为什么选 Zig 到为什么换 Rust,从 Zig 的内存管理困境到 Rust 的所有权模型如何解决 GC 与手动内存的冲突,从 AI 如何在 11 天内完成 535K 行代码的迁移,到这次重写对整个 JavaScript 生态意味着什么。


第一章:Bun 的起源与 Zig 的使命

1.1 从一个 Minecraft 项目的副产品说起

2021 年,Jarred Sumner 在做一个浏览器端的 Minecraft 风格体素游戏。代码量变大后,Next.js 的热重载需要 45 秒。这个等待时间让他崩溃,于是他开始把 esbuild 的 JSX/TypeScript 转译器从 Go 移植到 Zig。

三周后,一个能用的转译器诞生了。再过一个月,他读完了 WebKit 的源码,嵌入了 JavaScriptCore 引擎,Bun 的运行时雏形出现。

2022 年 7 月,Bun v0.1.0 发布。一周内拿到 20K GitHub Star。第一年,Jarred 一个人在奥克兰一间逼仄的公寓里完成了这个项目的核心部分——在 LLM 时代之前,纯手写。

1.2 为什么是 Zig?

Zig 的核心吸引力在于:它让你完全控制底层,同时保持代码的简洁性

对于 Bun 这样需要嵌入 JavaScriptCore(C++)、uWebSockets(C++)、BoringSSL(C)、SQLite(C)等大量 C/C++ 库的项目,Zig 的 extern "C" 兼容性和零隐藏控制流特性是理想选择。

Zig 的 defer 关键字是它的标志性设计:

fn foo(a: *TCPSocket) !void {
    const b = try do_something_with_a(a);
    // cleanup 自动执行,无需手动管理
}

对比 C++ 的 ~Destructor 和 Rust 的 Dropdefer 更加显式、更符合 Zig 的设计哲学——没有隐藏的控制流,没有魔法。

Jarred 自己也承认:如果没有 Zig,他不可能在一年内完成 Bun 的初始版本。

1.3 Bun 到底做了什么

Bun 的野心从一开始就很大——它不是在做一个 Node.js 替代品,而是在重新定义 JavaScript 工具链:

  • JavaScript/TypeScript/CSS 转译器、压缩器、打包器(对标 esbuild)
  • npm 兼容的包管理器(对标 npm/yarn/pnpm)
  • Jest 风格的测试运行器(对标 Jest)
  • Node.js API 兼容的运行时(对标 Node.js)
  • HTTP/1.1 和 WebSocket 客户端
  • 文件系统、网络、TLS 等 Node.js 模块的实现

一个工具,覆盖了 JavaScript 开发者的全部工具链需求。这种「全家桶」策略让 Bun 在 Node.js 统治了 15 年的生态中撕开了一道口子。


第二章:Zig 的代价——GC 与手动内存的致命冲突

2.1 稳定性噩梦

Bun v1.3.14 的发布说明中,列出了一长串让人心惊肉跳的 bug:

  • use-after-free:在 node:zlib 中调用 .reset() 时,异步 write() 还在 threadpool 上运行
  • use-after-free:在 node:http2 中,重入式 JS 回调触发 hashmap rehash,导致内部流指针失效
  • use-after-freeUDPSocket.send() 中,用户代码的 valueOf() 回调在数据捕获和实际发送之间 detach 了 ArrayBuffer
  • 内存泄漏crypto.scrypt 中,回调和受保护的 password/salt 缓冲区在输出缓冲区分配失败时从未释放
  • 内存泄漏tlsSocket.setSession() 每次调用泄漏一个 SSL_SESSION(约 6.5 KB)
  • 内存泄漏fs.watch() 的 watcher 在 .close() 后从未被 GC 回收
  • double-free:CSS 解析器在处理带 vendor 前缀和多层背景的 background-clip 时崩溃
  • 竞态条件MessageEvent 中 GC 标记线程在 BroadcastChannel 并发访问时观察到 torn variant

这些不是边缘 case,而是真实影响用户的生产环境 bug。

2.2 根本原因:JavaScript 的 GC 与 Zig 的手动内存管理之间的鸿沟

JavaScript 是垃圾回收语言。JavaScriptCore(以及 V8)对异常处理和 GC 有严格规则。而 Zig 和 C 一样,不帮你管理内存。

Bun 的核心挑战是:它需要同时正确管理两种内存——GC 管理的和手动管理的

每一个内存分配都必须被严格审查:

  • 这些字节在哪里被释放?
  • 如何确保只释放一次?
  • 是否正确检查了 JavaScript 异常?
  • 这个指针是否对保守栈扫描器可见?
  • 这是 GC 内存还是手动管理内存?

Bun 团队已经做了很多努力:

  • 为 Zig 编译器添加了 Address Sanitizer 支持
  • 使用 Fuzzilli(V8 和 JavaScriptCore 使用的 JS 引擎 fuzzer)24/7 模糊测试
  • 大量的端到端内存泄漏测试

但这还不够。正如 Jarred 所说:"我们的 bug 修复列表让我感觉很糟糕,我厌倦了每天晚上担心 Bun 的崩溃。"

2.3 Zig 的 defer 为什么不够

Zig 的 defer 在作用域结束时执行清理,这对于明确生命周期的场景(如解析器状态)很有效。但 Bun 的内存管理问题远比这复杂。

考虑这个场景:你把一个 *T 传给多个函数,怎么知道它什么时候不再被访问?如果某些函数在调用后还需要继续引用这块内存呢?

Bun 的做法是混合使用:

  • Arena 生命周期:作用域明确时使用(如解析器的 AST 节点)
  • 引用计数
  • 人工审查("pay really close attention")

对比 Rust 的做法——在类型系统中明确所有权关系——Zig 的方式本质上是靠纪律和代码审查来保证的。而纪律和审查总会在某个时刻失效。


第三章:为什么是 Rust?——编译器错误 vs 风格指南

3.1 核心论点

Bun v1.3.14 中列出的大量 bug 都是 use-after-free、double-free、"在错误路径上忘记释放"。在安全 Rust 中,这些是编译器错误。RAII 风格的自动清理(Drop)消除了手动管理的需求。

编译器错误是比风格指南更好的反馈循环。

这是一个简单但深刻的洞察。TigerBeetle 的 TigerStyle 是 Zig 中的风格指南示例,Google 有 31,000 字的 C++ 风格指南。但风格指南的问题在于执行——你怎么确保它被遵守?传统答案是代码审查加上 linter 和静态分析器的最佳努力执行。

Rust 的所有权系统把"风格指南"变成了"编译器强制执行的规则"。这不是一个增量改进,而是一个范式转换。

3.2 为什么不用 C/C++?

Bun 约 20% 的代码已经是 C++,而且它嵌入了多个 C/C++ 库。用 C++ 重写 Zig 部分是一个合理的选择——可以获得构造函数和析构函数,删除大量的 extern "C" 包装代码。

但 C++ 仍然依赖代码审查来执行风格指南。即使有 ASAN,内存损坏和内存泄漏仍然会发生。

Rust 提供了 C++ 无法提供的东西:在编译时保证内存安全

3.3 重写的传统智慧

历史上,重写通常是灾难性的。Bun 有 535,496 行 Zig 代码。一个小团队用另一种语言重写需要整整一年。这意味着冻结 bug 修复、安全修复和功能开发。

最安全的方法是机械式移植——从 Zig 到 Rust,行为变化最小,使用相同的测试套件。

但问题是:一年零用户可见影响是不现实的。

所以 Bun 团队最初的计划是通过代码风格强制执行来解决稳定性问题——他们已经在 Bun 的代码库中添加了 Rust 风格的智能指针。但 Jarred 说:"老实说,我不想这么做。自造的智能指针提供了比 Rust 更差的人体工程学,却没有任何保证。"


第四章:AI 重写 AI 基础设施——11 天完成 535K 行代码迁移

4.1 一个疯狂的想法

"如果我花一周时间测试一下 Anthropic 的新模型能否用 Rust 重写 Bun 呢?"

Jarred 用的是 Claude Fable 5 的预发布版本。他用大约 50 个动态工作流在 Claude Code 中持续运行了 11 天,完成了 Bun 从 Zig 到 Rust 的重写。

4.2 关键决策:一次性重写 vs 增量重写

一次性重写更好。 增量重写会添加临时代码,你希望它们最终被删除,而且在短期到中期内会很痛苦。

Jarred 之前在没有 LLM 的情况下,把 esbuild 的转译器从 Go 移植到 Zig 时就发现:一次性重写虽然风险更高,但长期来看更干净。

4.3 策略:看起来像转译的重写

策略很简单:做一个看起来像把 Zig 代码转译成 Rust 的重写。然后在 Bun v1.4 发布后,逐步重构以减少 unsafe 使用并使其更符合 Rust 惯用法。

这确保了团队在重写后仍然能够维护代码。如果重写产出的代码完全是 Rust 风格但与原始架构毫无关系,维护团队将面临巨大的认知负担。

4.4 工作流设计

每个工作流都是一个循环:

// 伪代码
let task;
while ((task = todoList.pop())) {
    const result = task();
    const feedback = await Promise.all([review(result), review(result)]);
    await apply(feedback, result);
}

具体工作流包括:

  1. 生成移植指南:将 Zig 模式和类型映射到 Rust 模式和类型
  2. 机械式移植:将每个 .zig 文件移植为 .rs 文件,匹配 PORTING.md 和 LIFETIMES.tsv
  3. 修复编译错误:让每个 crate 的编译器错误消失
  4. 子命令可用:让 bun testbun build 等子命令工作
  5. 测试通过:让 Bun 整个测试套件中的每个测试通过
  6. 重构和清理:多个大型重构和清理 pass

4.5 对抗性审查:如何审查百万行 AI 生成的代码

如何审查一个增加 100 万行的 PR?如何建立信心来负责任地合并大量 LLM 编写的代码?

Bun 的答案是三个支柱:

1. 语言无关的测试套件

Bun 的测试套件是用 TypeScript 写的,不依赖运行时的编程语言。这意味着它可以直接用于验证 Rust 版本的正确性。百万级断言提供了强大的回归检测能力。

2. 对抗性审查

在独立的上下文窗口中,让 Claude 穷尽所有理由来说明为什么变更会产生 bug 或不能工作。这类似于代码审查中的"红队"思维。

3. 分离上下文窗口

写代码的 Claude 和审查代码的 Claude 在不同的上下文中。写代码的 Claude 想合并代码(这可能产生偏见),审查的 Claude 想找问题。

1 个实现者 + 2 个或更多对抗性审查者
实现者不审查,审查者不实现

这不是一个新想法——人类代码审查中也是这样做的。但在 AI 辅助开发中,执行这一点更加自然,因为 AI 模型可以在隔离的上下文中运行。

4.6 修复过程,而不是修复代码

当出现问题时,Bun 团队不手动修复生成的代码,而是修复生成代码的过程。这是一个关键的思维转换:

  • 传统方式:AI 生成代码 → 发现 bug → 人类手动修复 bug
  • Bun 的方式:AI 生成代码 → 发现 bug → 修改 prompt/工作流/约束条件 → 重新生成

这确保了代码质量和一致性,而不是在 AI 生成的代码上叠加人工修补。


第五章:Bun v1.3.x 的技术亮点

在 Rust 重写完成之前,Bun 的 v1.3.x 系列已经展现了惊人的功能密度。

5.1 Bun.Image:内置图片处理

Bun v1.3.14 引入了内置的图片处理 API,支持 JPEG、PNG、WebP、GIF、BMP,以及 macOS/Windows 上的 HEIC、AVIF、TIFF。

// 调整大小并转换为 WebP
await Bun.file("photo.jpg")
  .image()
  .resize(1024, 1024, { fit: "inside" })
  .rotate(90)
  .webp({ quality: 85 })
  .write("thumb.webp");

// 从上传文件生成缩略图
return new Response(new Bun.Image(upload).resize(200).jpeg());

性能对比(vs sharp 0.34.5):

  • metadata():快 70 倍(0.004ms vs 0.28ms)
  • 1080p PNG → 400×400 → JPEG:快 1.38 倍
  • 4K JPEG → 800×450 → JPEG:快 1.27 倍

性能优势来自 i16 定点 SIMD resize 内核、JPEG IDCT 缩放、零拷贝 ArrayBuffer 借用,以及为 resize scratch memory 预分配的单一 arena。

5.2 全局虚拟存储:7 倍热安装加速

bun install --linker=isolated 现在支持共享全局虚拟存储。包不再从缓存克隆到每个项目的 node_modules,而是物化到一个全局 /links/ 目录,每个项目的 node_modules/.bun/@ 成为指向它的符号链接。

在 macOS APFS 上,clonefileat() 持有一个全卷级内核锁,使得并行化无效。全局存储完全消除了这些调用。

基准测试(~1,400 个包,Apple Silicon macOS):

  • 安装前:823ms(wall time),1,387 次 clonefileat 调用
  • 安装后:115ms(wall time),0 次 clonefileat 调用
  • 提升 7 倍

5.3 HTTP/3 (QUIC) 支持

Bun.serve 现在支持 HTTP/3 over QUIC。启用后,Bun 在同一端口上同时绑定 TCP(HTTP/1.1+2)和 UDP(HTTP/3):

Bun.serve({
  port: 443,
  tls: { cert, key },
  http3: true,
  fetch(req) {
    return new Response("hi");
  },
});

性能基准(Linux x64,单进程,loopback):

  • HTTP/3:509,135 req/s(静态路由)
  • HTTPS/1.1:189,130 req/s
  • HTTP/1.1:239,476 req/s

HTTP/3 比 HTTPS/1.1 快约 2.7 倍。

5.4 其他亮点

  • v1.3.13bun test --parallel/--isolate/--shard/--changed,17 倍更低内存的 tarball 流式传输,5.5 倍更快的 gzip(zlib-ng)
  • v1.3.12:终端渲染 Markdown(bun ./file.md),Bun.WebView 无头浏览器自动化,Bun.cron() 进程内调度器
  • v1.3.11:Linux 上二进制缩小 4MB,Bun.cron 支持 OS 级 cron 任务
  • v1.3.10:原生 REPL,--compile --target=browser 生成自包含 HTML,TC39 标准 ES 装饰器,Windows ARM64 支持
  • v1.3.9:并行/顺序运行多个脚本,ESM 字节码编译
  • v1.3.8:内置 CommonMark Markdown 解析器,bun build --metafile-md 生成 LLM 友好的模块图元数据

第六章:收购与战略——Anthropic 为什么赌 Bun

6.1 Claude Code 的基础设施赌注

Claude Code 作为 Bun 可执行文件分发给数百万用户。如果 Bun 崩溃,Claude Code 就崩溃。Anthropic 有直接动机保持 Bun 的优秀。

这不是一个慈善赞助,而是一个战略投资。AI 编程工具的基础设施层在 agent 写代码时变得更加重要。

6.2 为什么不是"构建云托管产品"

Bun 团队最初的可持续性答案一直是"我们最终会构建一个云托管产品"。但世界变了。AI 编程工具正在大规模改变开发者的工作方式,基础设施层在 agent 写代码时更加重要。

强迫自己走既定道路,当 AI 编程工具变得这么好、这么快时,感觉不对。

6.3 Bun 的路线图不变

收购后的承诺:

  • Bun 保持开源和 MIT 许可
  • 继续积极维护
  • 同一个团队继续开发
  • 继续在 GitHub 上公开构建
  • 路线图继续聚焦高性能 JavaScript 工具、Node.js 兼容性,以及取代 Node.js 作为默认服务端运行时

第七章:对 JavaScript 生态的影响

7.1 Node.js 的位置更加危险

Bun 加上 Anthropic 的资源,Node.js 面临的竞争压力前所未有。Bun 已经在以下方面超越或追平 Node.js:

  • 启动速度:JavaScriptCore 比 V8 快约 4 倍
  • 包安装速度:bun install 比 npm 快 10-100 倍
  • 内置工具:打包器、测试运行器、图片处理、Markdown 渲染、Cron 调度器
  • TypeScript 支持:原生支持,无需转译
  • 单文件可执行bun build --compile 生成自包含二进制

7.2 Rust 在 JavaScript 基础设施中的崛起

Bun 的重写不是孤例。React Compiler 也从 TypeScript 迁移到 Rust(获得 3-10 倍性能提升),Ruff 用 Rust 重建了 Python 的整个开发工具链。

趋势很清楚:性能关键的 JavaScript 基础设施正在用 Rust 重写。 Rust 的内存安全保证、零成本抽象和优秀的 C/C++ FFI 使其成为嵌入式和系统编程的理想选择。

7.3 AI 辅助重写的新范式

Bun 的 Rust 重写展示了一种新的软件工程范式:

  1. 用 AI 完成大规模机械式代码迁移
  2. 用语言无关的测试套件验证正确性
  3. 用对抗性审查确保质量
  4. 修复生成过程而非修复代码

这不是"让 AI 写代码"这么简单。它是一种系统化的方法论,将 AI 的能力与人类的判断力结合在一起。11 天完成 535K 行代码的重写,同时保持相同的功能和性能——这在传统工程中是不可想象的。


第八章:实战指南——如何迁移到 Bun

8.1 安装

# macOS/Linux
curl -fsSL https://bun.sh/install | bash

# npm
npm install -g bun

# Windows
powershell -c "irm bun.sh/install.ps1|iex"

# Docker
docker pull oven/bun
docker run --rm --init oven/bun

8.2 基础用法

// server.ts - 一个完整的 HTTP 服务器
const server = Bun.serve({
  port: 3000,
  fetch(req) {
    const url = new URL(req.url);
    
    if (url.pathname === "/") {
      return new Response("Hello from Bun!");
    }
    
    if (url.pathname === "/api/users") {
      return Response.json([
        { id: 1, name: "Alice" },
        { id: 2, name: "Bob" },
      ]);
    }
    
    return new Response("Not Found", { status: 404 });
  },
});

console.log(`Server running at http://localhost:${server.port}`);

8.3 图片处理

// 生成带水印的缩略图
async function createThumbnail(input: string, output: string) {
  await Bun.file(input)
    .image()
    .resize(800, 600, { fit: "cover" })
    .jpeg({ quality: 85 })
    .write(output);
  
  const meta = await Bun.file(output).image().metadata();
  console.log(`Thumbnail created: ${meta.width}x${meta.height}`);
}

// 生成 blur-up 占位符
const placeholder = await Bun.file("hero.jpg").image().placeholder();
// 返回 thumbhash data URL

8.4 测试

// test/example.test.ts
import { describe, test, expect } from "bun:test";

describe("math operations", () => {
  test("addition", () => {
    expect(1 + 1).toBe(2);
  });
  
  test("async operation", async () => {
    const response = await fetch("http://localhost:3000");
    expect(response.status).toBe(200);
    expect(await response.text()).toBe("Hello from Bun!");
  });
});

// 运行:bun test
// 并行运行:bun test --parallel
// 按路径过滤:bun test --path-ignore-patterns="**/slow/**"

8.5 包管理

# 安装依赖(比 npm 快 10-100 倍)
bun install

# 添加依赖
bun add express

# 添加开发依赖
bun add -d typescript @types/node

# 运行脚本
bun run dev
bun run build

总结:基础设施的新范式

Bun 的故事不仅仅是一个 JavaScript 运行时的技术选型。它揭示了 2026 年软件工程的三个关键趋势:

1. AI 公司正在重新定义基础设施的构建方式。 Anthropic 收购 Bun 不是为了卖云服务,而是因为 AI 编程工具需要更好的运行时。当 AI 成为代码的主要消费者时,基础设施的质量直接影响 AI 的输出质量。

2. Rust 正在成为系统级 JavaScript 基础设施的首选语言。 从 React Compiler 到 Ruff 到 Bun,Rust 的内存安全保证和零成本抽象正在取代 C++ 和 Zig,成为性能关键的 JavaScript 工具的实现语言。

3. AI 辅助的大规模代码迁移是可行的。 Bun 的 Rust 重写证明:在正确的约束条件下(语言无关的测试套件、对抗性审查、过程修复而非代码修复),AI 可以在极短时间内完成传统工程需要一年才能完成的工作。

Bun 的 CLI 月下载量已超过 2200 万。Claude Code、OpenCode 等 AI 编程工具选择 Bun 作为运行时。Vercel、Railway、DigitalOcean 提供一等公民支持。

当 AI 重写 AI 的基础设施时,JavaScript 的未来正在被重新定义。


参考资源

  • Bun 官方博客:https://bun.com/blog
  • Bun GitHub:https://github.com/oven-sh/bun
  • "Rewriting Bun in Rust":https://bun.com/blog/bun-in-rust
  • "Bun is joining Anthropic":https://bun.com/blog/bun-joins-anthropic
  • Bun v1.3.14 Release Notes:https://bun.com/blog/bun-v1.3.14

推荐文章

如何实现虚拟滚动
2024-11-18 20:50:47 +0800 CST
Go 1.23 中的新包:unique
2024-11-18 12:32:57 +0800 CST
使用 sync.Pool 优化 Go 程序性能
2024-11-19 05:56:51 +0800 CST
详解 Nginx 的 `sub_filter` 指令
2024-11-19 02:09:49 +0800 CST
程序员茄子在线接单