当 AI 用 6 天重构了互联网的 JavaScript 运行时:Bun 的 Zig → Rust 迁移完整复盘
引言:一场史无前例的代码大迁徙
2026年5月14日,GitHub 上发生了一件足以载入软件工程史册的事件:一条编号为 #30412 的 Pull Request 被合并进 Bun 的主分支。这条 PR 包含了超过 100 万行用 Rust 重写的代码、6755 次提交,而完成这一切的,不是几十名工程师组成的团队,而是 Claude Code 智能体在短短 6~9 天内自动生成的。
Bun,这个曾以 Zig 语言为技术标签、每月下载量超过 700 万次、GitHub 星标超过 9.2 万的 JavaScript 运行时,其核心运行时正式从 Zig 语言迁移到了 Rust。这不仅是语言层面的切换,更是一次关于 AI 编程边界、安全性本质、以及"代码信任"哲学的深度实验。
本文将从技术内幕出发,系统拆解这次迁移的全貌:为什么 Bun 要离开 Zig?Claude Code 是怎么完成这个看似不可能的任务的?迁移的代价是什么?13000 个 unsafe 代码块意味着什么?以及,作为程序员,我们到底应该从这件事里学到什么。
一、背景:为什么 Bun 选择了 Zig,又为什么最终离开
1.1 Zig 的野心与 Bun 的选择
Zig 语言诞生于 2016 年,由 Andrew Kelley 创立。它的核心设计哲学是"没有隐藏的控制流、没有隐藏的内存分配、没有宏系统的复杂性"。对于需要极致性能的 JavaScript 运行时来说,这些特性极具吸引力。
Bun 的创始人 Jarred Sumner 在 2024 年选择了 Zig 作为核心实现语言。Zig 让 Bun 实现了:
- 极快的启动速度:Bun 的冷启动时间仅为 45ms,而 Node.js 需要 320ms
- 对 JavaScript 引擎(JavaScriptCore)的精细控制:绕过 V8 直接对接底层,减少了抽象层带来的性能损耗
- C/C++ 互操作的零开销绑定:可以无缝复用现有的 C 库
这些优势让 Bun 成为 JavaScript 运行时中性能最耀眼的那颗星,吸引了大量对性能有极致追求的开发者。
1.2 Zig 的困境:内存泄漏与调试噩梦
然而,随着 Bun 影响力的扩大和用户规模的增长,Zig 版本的问题也逐渐暴露出来。
内存泄漏是最大的噩梦。由于 Zig 依赖手动内存管理,开发团队花费了大量时间去调试以下类型的问题:
- 释放后使用(Use-After-Free):对象被释放后,代码仍持有对它的引用
- 双重释放(Double-Free):同一块内存被释放两次,导致堆损坏
- 数据竞争(Data Races):多线程场景下对共享数据的非同步访问
这些 bug 的可怕之处不仅在于难以复现,更在于:它们往往只在特定的并发条件、特定的输入组合下才会触发,而在日常开发测试中完全隐身。等到在生产环境爆发时,影响面已经无法估量。
1.3 Claude Code 的"内存危机"
真正让 Anthropic 下定决心的导火索,是 Claude Code 自身的遭遇。
Claude Code 是 Anthropic 推出的 AI 编程助手,它内置使用了 Bun 作为运行时引擎。有开发者报告称,在持续使用 Claude Code 的过程中,内存占用从初始的 1.7GB 飙升到 14GB,甚至更高。这直接影响了 Claude Code 的稳定性和用户体验。
讽刺的一幕出现了:Anthropic 的 AI,因为 Bun(Zig版本)的内存问题而深受困扰,最终的解决方案竟然是——让 Claude 亲手重写 Bun。
2025年12月,Anthropic 宣布收购 Bun。
二、技术内幕:Claude Code 如何在 6 天内完成 96 万行代码迁移
2.1 Anthropic 的 Agent 编排策略
这并不是一个简单的"把 Zig 代码复制粘贴成 Rust"的机械过程。Anthropic 为这次迁移设计了复杂的多 Agent 协作流水线:
┌─────────────────────────────────────────────────────────────┐
│ 迁移流水线架构 │
├─────────────────────────────────────────────────────────────┤
│ Pipeline Stage 1: Zig → Rust 翻译 │
│ ├── Agent: Translation Agent (逐文件翻译) │
│ ├── 输入: Zig 源文件 + Rust 惯用法指南 │
│ └── 输出: 初始 Rust 草稿版本 │
├─────────────────────────────────────────────────────────────┤
│ Pipeline Stage 2: 验证与修复 │
│ ├── Agent: Validation Agent (测试验证) │
│ ├── Agent: Fix Agent (编译错误修复) │
│ └── Agent: Lifetime Classifier (生命周期分类) │
├─────────────────────────────────────────────────────────────┤
│ Pipeline Stage 3: 专项审计 │
│ ├── Agent: Unsafe Auditor (unsafe 代码审计) │
│ ├── Agent: Windows Bug Hunter (Windows 平台兼容性) │
│ └── Agent: Performance Regression Checker │
├─────────────────────────────────────────────────────────────┤
│ Pipeline Stage 4: 收敛与合并 │
│ ├── Orchestrator: 结果汇总与冲突解决 │
│ └── Git: 自动生成 PR,提交 merge 请求 │
└─────────────────────────────────────────────────────────────┘
这种流水线设计的精妙之处在于:每个 Agent 专注一个职责。Translation Agent 负责忠实地将 Zig 代码翻译为 Rust 语法结构,Fix Agent 负责修复编译错误,Unsafe Auditor 专门扫描 unsafe 块并评估其安全性,而 Windows Bug Hunter 则在 Windows 平台上运行集成测试,寻找只在 Windows 上触发的兼容性问题。
2.2 忠实的移植策略:一把双刃剑
Bun 团队发布的迁移指南中,最核心的原则是:尽可能忠实地移植 Zig 代码。
这意味着:
- 保持相同的架构设计
- 保持相同的数据结构
- 保持相同的模块边界
- 逐文件进行转换
这个策略的优点是显而易见的:风险最小化。忠实移植意味着保留已经经过生产环境验证的行为逻辑,只需要解决"语言翻译"本身的问题,而不需要同时承担"行为变更"带来的风险。
然而,这个策略也埋下了一个伏笔,后文会详细展开。
2.3 惊人的数字:9 天,100 万行,6755 次提交
让我们用数字来感受这次迁移的规模:
| 指标 | 数值 |
|---|---|
| 新增代码行数 | ~100 万行 Rust |
| 总提交次数 | 6755 次 |
| 完成时间 | |
| 测试通过率 | 99.8% |
| PR 文件数 | 覆盖 700+ 个文件 |
| 二进制体积变化 | 缩小 3~8 MB |
这个规模的代码生成,在传统开发模式下,可能需要几十名工程师工作数月才能完成。而 Claude Code 智能体在不到一周的时间里就交付了。
更令人惊叹的是 PR #30412 的体量——由于包含了超过 100 万行新增代码,它直接把 GitHub 的渲染系统撑爆了,PR 页面一度无法正常加载。
三、99.8% 的测试通过率:它到底证明了什么?
3.1 官方叙事:Rust = 内存安全
Bun 团队给出的迁移核心理由是内存安全。
Jarred Sumner 多次在公开场合表示:"过去这些年里,Zig 代码库让团队花费了大量时间去调试内存相关问题。我们迁移到 Rust 的核心理由,是借助编译器提供的内存安全保障,从根本上杜绝 dangling pointer、buffer overflow 等常见漏洞。"
这个说法在逻辑上是成立的。Rust 的所有权系统(Ownership)、借用检查器(Borrow Checker)和生命周期(Lifetime)分析,能够在编译时捕获大量内存错误,而不需要依赖运行时垃圾回收器或手动引用计数。
对于一个每天处理数十亿次请求的 JavaScript 运行时来说,这种编译期安全保障的价值确实难以估量。
3.2 测试通过率揭示的真相
然而,当开发者 dreamreal 在长文《Bun Has Been Converted to Rust. Now What?》中提出一个关键问题时,整个社区沉默了:
"99.8% 的测试通过率,到底证明了什么?"
答案是:它只能证明新实现与旧实现的行为一致。
换句话说:Rust 版本和 Zig 版本在功能上等效,在对外暴露的 API 行为上完全一致。这当然很重要,但它不能证明新实现是安全的、更好的,甚至是优秀的。功能等效和安全性是两个完全不同的维度。
这就好比你把一篇英文文章逐句翻译成中文,然后说"翻译版本和原文一样好"——但如果原文本身就存在逻辑漏洞,翻译版本会自然地继承这些漏洞。
3.3 99.8% 的另一面:0.2% 未通过测试去哪儿了
Bun 团队坦诚地表示,有 0.2% 的测试用例未通过。这些未通过的测试主要涉及:
- 边缘场景:特定的边界条件、极端输入
- 平台特定行为:某些只在特定操作系统或特定 libc 版本上出现的行为差异
关键问题在于:未定义行为(Undefined Behavior)从来不会通过测试失败来主动暴露自己。它更可能以另一种方式出现——比如 18 个月后,在某个从未在 CI 环境里运行过的 libc 实现上,被发现并登记为一个 CVE 漏洞。
四、13000 个 unsafe 代码块:Rust 安全神话的裂缝
4.1 对比 uv:13000 vs 73
这是整个事件中最引人深思的数字。
在 Bun Rust 版本中,分布在 700 多个文件里的 unsafe 代码块超过 13000 个。
作为对比,体量和复杂度相近的知名 Rust 项目 uv(Python 包管理器,由 Astral 开发),整个项目中只有 73 个 unsafe 代码块。
两者相差两个数量级。
4.2 unsafe 代码在 Rust 中意味着什么
Rust 的内存安全保障建立在一条关键假设之上:所有内存安全保证都依赖于 safe Rust 的类型系统。当代码处于 safe 上下文时,Rust 编译器会通过所有权和借用检查确保你不会做出任何不安全的操作。
但 unsafe 代码块是 Rust 向后兼容 C/C++ 的桥梁,也是性能优化的利器。在 unsafe 块中,你可以:
// unsafe 块中允许的操作
unsafe {
// 解引用原始指针
let raw_ptr = ptr::read_nonoverlapping::<u64>(data_ptr, 8);
// 调用 unsafe 函数
some_unsafe_function();
// 实现 unsafe trait
impl UnsafeTrait for MyType { ... }
// 访问或修改可变静态变量
static mut COUNTER: u64 = 0;
COUNTER += 1;
}
问题在于:unsafe 块中的错误不会被 Rust 编译器捕获。一个 unsafe 块中的指针错误,可能导致周围所有依赖 Rust 类型系统保护的 safe 代码失去保障。这就是为什么 Rust 社区有一句广泛流传的话:
"unsafe 代码中的每一个 bug,都可能变成整个系统的安全漏洞。"
4.3 为什么会产生 13000 个 unsafe
答案就藏在 Bun 团队的"忠实移植"策略中。
Zig 代码本身是手动内存管理的,它依赖于程序员的纪律性来保证正确性。当这段代码被"忠实移植"到 Rust 时,Rust 的借用检查器会拒绝接受其中大量不满足借用规则的模式。
最直接的解决方案是什么?在需要绕过借用检查器的地方,直接包裹一个 unsafe {} 块。
// 忠实移植 Zig 代码时的典型场景
// Zig 原版:直接操作原始指针
fn zig_style_operation(ptr: *mut u8, len: usize) -> u64 {
// Zig: 指针操作完全由程序员保证正确性
let result = unsafe { std::ptr::read_unaligned(ptr as *const u64) };
result
}
// Rust 忠实移植版:不得不用 unsafe 绕过借用检查
fn rust移植版_operation(ptr: *mut u8, len: usize) -> u64 {
// 借用检查器不认可这种操作,用 unsafe 绕过
unsafe {
std::ptr::read_unaligned(ptr as *const u64)
}
}
逐文件翻译的 LLM 最擅长的是什么?是忠实地将一种语法翻译为另一种语法。 它不擅长(或者说,根据迁移指南不被鼓励)的是:从头设计符合 Rust 惯用写法的、安全的实现方案。
4.4 unsafe 不是"后续清理"就能解决的
最常见的辩护理由是:"这只是早期阶段,后续 PR 会逐步消除 unsafe 代码。"
然而,Todd Smith 等研究者指出:消除 unsafe 代码并不是简单的代码清理工作,而是一个尚未被彻底解决的研究难题。
验证 Rust 中一段 unsafe 代码是否真正安全,需要:
- 理解该 unsafe 块的所有依赖关系
- 逐个论证每个操作的安全性
- 用形式化方法证明没有未定义行为
Amazon 联合 Rust 基金会发起的 rustbelt 项目,正是为了验证 Rust 标准库中的 unsafe 代码。这个项目规模远比 Bun 的运行时代码小,也经过了更严格的专家级别人工审查,而且代码是由人工编写的——即便如此,仍然发现了多个 CVE 级别的漏洞。
学术界当前最先进的方法,也不过是半自动化分析工具和需要人工编写形式化规范的实验性验证器。
不存在一个"一键让 unsafe 代码变安全"的工具。在可预见的未来,也看不到这种工具出现的可能。
五、从 Zig 到 Rust 迁移的工程启示
5.1 AI 辅助代码迁移的正确姿势
这次迁移给整个行业上了一课:忠实的翻译并不等于正确的迁移。
Todd Smith 的建议是:
"根本不要自动迁移内存不安全的代码。应该先为产品的外部可观察行为编写详细规范,然后告诉 Agent:现有代码只能作为参考资料,用来补充实现细节,而真正的主要依据应该是规范本身。"
这个建议揭示了 AI 辅助迁移的正确框架:
┌─────────────────────────────────────────────────────────────┐
│ 规范优先的 AI 辅助迁移方法论 │
├─────────────────────────────────────────────────────────────┤
│ Step 1: 外部行为规范 (Specification First) │
│ ├── 定义模块的公开 API 契约 │
│ ├── 描述边界条件与错误处理行为 │
│ └── 量化性能基线与资源约束 │
│ ↓ │
│ Step 2: 参考代码辅助实现 │
│ ├── 将 Zig 实现作为"实现参考"而非"目标规范" │
│ ├── 鼓励 LLM 在 Rust 惯用法框架内寻找等效实现 │
│ └── 禁止在无法通过借用检查的地方使用 unsafe 绕过 │
│ ↓ │
│ Step 3: 基于规范的验证 │
│ ├── Property-based testing(属性测试) │
│ ├── Fuzzing(模糊测试) │
│ └── 形式化验证(针对关键 unsafe 路径) │
└─────────────────────────────────────────────────────────────┘
5.2 测试通过率与安全性的关系
| 测试维度 | 测试通过率能证明? | 测试通过率不能证明? |
|---|---|---|
| 功能正确性 | ✅ 是 | |
| 性能不退化 | ✅ 大致可以(benchmark) | |
| 内存安全性 | ❌ 不能 | 需要形式化验证 |
| 并发安全性 | ❌ 不能 | 需要专门的并发测试套件 |
| 边界情况覆盖 | ❌ 取决于测试质量 | 需要变异测试 |
| 平台兼容性 | ❌ 取决于 CI 覆盖度 | 需要多平台测试矩阵 |
5.3 Rust 惯用法 vs. 逐字翻译
真正的 Rust 风格代码,会充分利用 Rust 的类型系统和所有权模型来实现内存安全:
// ❌ 忠实移植 Zig 风格(有大量 unsafe)
struct BufferZigStyle {
ptr: *mut u8,
len: usize,
capacity: usize,
}
// ✅ Rust 惯用法(利用类型系统保证安全)
struct BufferRustStyle {
data: Vec<u8>, // 自动管理内存,无需 unsafe
}
impl BufferRustStyle {
fn new() -> Self {
Self { data: Vec::new() }
}
fn with_capacity(capacity: usize) -> Self {
Self { data: Vec::with_capacity(capacity) }
}
// Rust 的 Iterator 保证了边界安全,无需 unsafe
fn sum(&self) -> u64 {
self.data.iter().map(|&x| x as u64).sum()
}
}
六、代码生成速度 vs. 代码审查速度:不对称危机
6.1 指数级扩张的生成能力
这次事件最令人不安的发现,不是 13000 个 unsafe,而是这种不对称正在成为常态。
Claude Code 在这次迁移中平均每天生成超过 1100 次提交,每天产出 10~15 万行新代码。而一名经验丰富的 Rust 工程师,平均每天能够深入审查的代码行数大约在 200~500 行之间(对于关键的安全相关代码,这个数字会更低)。
代码生成能力和代码审查能力之间的差距,不是线性的,而是指数级扩张的。GPT-3.5 能写的代码量已经超过任何人能读的。GPT-4 翻了 10 倍。Claude 3.5 继续扩张。随着模型能力的增长,这个差距只会越来越大。
6.2 谁来审查这些代码?
在 Hacker News 的讨论中,这个问题引发了最激烈的辩论:
"一个智能体在 9 天内生成的一百万行代码,到底是谁审核的?"
答案是:没有。至少没有以审查关键基础设施代码应有的方式完整审核过。
Bun 团队目前的信心来源主要是测试套件。但如前文所述,测试套件从来没有验证过这次迁移最核心的目标——内存安全性。
这不是 Bun 独有的问题。Bun 可能只是迄今为止规模最大、曝光度最高的案例。类似的 AI 辅助重写正在 Cloudflare、Ladybird 浏览器等多个项目中进行。
6.3 我们能做什么?
作为程序员和工程团队,我们可以从以下几个维度来应对:
1. 建立 AI 生成代码的审查规范
AI 生成代码审查清单:
□ 每一处 unsafe 代码是否有明确的理由和注释?
□ 该 unsafe 块是否可以被 safe Rust 等效替代?
□ 所有权和生命周期是否被明确建模?
□ 并发安全性是否有专门的测试覆盖?
□ 边界条件和错误处理是否被充分测试?
□ 是否有性能回归测试?
2. 规范先行,而不是代码先行
对于涉及语言迁移的重大重构,应该先定义详细的外部行为规范,再让 AI 参考现有实现进行迁移。规范才是最终的正确性标准,而不是"参照旧代码"。
3. 建立 AI 辅助的验证工具链
学术界正在积极探索的方向:
- Mirage:基于 Rust 的轻量级操作系统,使用自动化证明辅助
- Prusti:用于验证 Rust 程序规范的形式化工具
- SAVI:针对 unsafe Rust 的自动化验证框架
4. 将 unsafe 代码视为技术债务
在项目层面,unsafe 代码应该有明确的追踪和管理机制。每个 unsafe 块都应该被视为需要定期审查的技术债务,而不是"已解决"的问题。
七、对 JavaScript 生态的影响与展望
7.1 Rust 版本 Bun 的性能数据
尽管存在安全性争议,但客观来看,Rust 版本 Bun 在性能上确实有所提升:
| 指标 | Zig 版本 | Rust 版本 | 变化 |
|---|---|---|---|
| Linux x64 二进制体积 | ~93 MB | ↓ 3~8 MB | |
| JavaScriptCore 初始化时间 | 基准 | 持平~略快 | — |
| 文件 I/O 吞吐量 | 基准 | 持平 | — |
| HTTP 服务器吞吐量 | 基准 | 持平~略高 | — |
| 内存泄漏问题 | 多次出现 | 修复 | ✅ |
Rust 版本修复了多个长期存在的内存泄漏问题,这是毫无疑问的进步。关键问题在于:它是否解决了根本原因,还是只是碰巧修复了表面症状?
7.2 Anthropic 的战略意图
Anthropic 收购 Bun 并非单纯的财务投资,而是有明确的战略意图:
- Claude Code 的执行层:Bun 将作为 Claude Code 的核心运行时,提供更快的启动速度和更稳定的内存表现
- AI 工具链整合:Anthropic 计划将 Bun 的基础设施能力整合到更广泛的 AI 编程工具中
- JavaScript 生态渗透:通过 Claude Code 的广泛使用,Anthropic 将获得对 JavaScript 生态的深度影响力
这也意味着,Bun 的这次迁移不只是 Bun 自身的事——它直接关系到全球数百万 Claude Code 用户的使用体验。
7.3 对其他语言迁移项目的启示
除了 Bun,2026 年还有多个大规模语言迁移项目正在进行中:
| 项目 | 从 | 到 | 规模 | 方式 |
|---|---|---|---|---|
| Codex CLI | TypeScript/Node.js | Rust | ~15万行 | AI 辅助 |
| LiteLLM 核心 | Python | Rust | ~5万行 | AI 辅助 |
| Ladybird 浏览器 | C++ | Rust | 数百万行 | 渐进迁移 |
这些项目的共同特点是:规模巨大、依赖 AI 辅助、目标是内存安全和性能提升。
八、冷静思考:AI 编程的边界在哪里
8.1 AI 擅长什么
经过大量实践,AI 编程工具的能力边界已经比较清晰了:
✅ AI 非常擅长:
- 模板化代码生成(CRUD、测试用例、样板代码)
- 算法实现翻译(将一种语言的实现翻译为另一种语言的等效实现)
- Bug 修复(给定错误信息和上下文,生成修复建议)
- 代码解释和文档生成
- 简单的重构操作
8.2 AI 不擅长什么
❌ AI 目前仍然不擅长:
- 理解复杂系统的全局行为:局部正确的代码可能导致全局错误
- 发现深层的安全漏洞:内存安全漏洞往往需要理解整个调用链和数据流
- 设计新的架构方案:创新性的系统设计仍然是人类的领域
- 验证 unsafe 代码的正确性:这是形式化方法的专属领地
8.3 这次迁移教会我们的最重要一课
当 Jarred Sumner 在合并 PR 后说"我们已经好几个月没有亲手敲代码了"时,整个社区的反应是复杂的。
有人看到了希望:AI 终于可以承担大规模的基础设施重写工作,释放人类工程师去做更有创造性的工作。
但更多人看到了警讯:如果连代码安全的验证都无法跟上代码生成的速度,我们如何确保 AI 生成的代码真的值得信任?
这次 Bun 迁移给出的答案,不是"AI 可以",也不是"AI 不行"。
它给出的答案是:AI 生成代码的速度正在指数级增长,而人类审查代码的能力并没有同步增长。这种不对称,才是我们真正需要解决的问题。
结语:不是终点,而是开始
Bun 的 Zig → Rust 迁移,是 2026 年技术界最重要的工程实验之一。它证明了 AI 在大规模代码生成方面的惊人能力,也暴露了 AI 编程在安全验证方面的深层挑战。
从好的方面看,这次迁移确实解决了 Bun 长期存在的内存泄漏问题,并且二进制体积还略有缩小。在保持功能完整性的同时完成如此大规模的语言切换,本身就是工程上的成就。
但从挑战的方面看,13000 个 unsafe 代码块、无法被测试套件证明的内存安全性、以及代码审查速度与生成速度之间的巨大缺口,都在提醒我们:AI 编程的下半场,不是让 AI 生成更多代码,而是建立与之配套的代码信任体系。
对于所有正在使用或计划使用 AI 辅助编程的团队来说,Bun 的经验教训是宝贵的:
不要用测试通过率来证明安全性。测试通过率只能证明行为一致性。内存安全性需要形式化验证、需要专业的 unsafe 审计、需要完善的模糊测试和属性测试。
规范优先,而不是代码优先。先定义正确,再生成代码。
AI 是强大的工程劳动力,但不是安全的保证人。将 AI 生成的代码视为"需要严格审查的高风险代码",而不是"可以信任的直接可用的代码"。
这次迁移真正的遗产,不是那 100 万行 Rust 代码,而是一个关于代码信任的新问题。
当代码可以由 AI 在 6 天内生成时,谁来保证它是安全的?
这个问题没有简单的答案。但认识到它的存在,已经是解决问题的第一步。
本文参考资料:
- Bun PR #30412: https://github.com/oven-sh/bun/pull/30412
- dreamreal, "Bun Has Been Converted to Rust. Now What?", https://bytecode.news/posts/2026/06/bun-has-been-converted-to-rust-now-what
- Jarred Sumner, Bun 创始人公告 (2026-05-14)
- Todd Smith, Unsafe Rust Verification (RustBelt Project)