Bun.js 的 Rust 重生:6天、96万行代码、Claude Code 亲手重写自己——AI 时代的软件工程范式转移(2026完全指南)
摘要:2026年5月,Bun.js 创始人 Jarred Sumner 在 X 上宣告:"如果我们合并 Rust 重写版本,这将是 Zig 的最后一个版本。" 这场从 Zig 到 Rust 的迁移,只花了大约六天,涉及 96 万行代码。更荒诞的是——Claude Code 被 Bun 的内存泄漏坑惨了,然后 Anthropic 让 Claude 去重写 Bun,最后 Bun 再继续回头支撑 Claude Code。本文深度解析这场重写的技术细节、架构决策、性能对比,以及它揭示的 AI 时代软件工程新范式。
目录
- 序幕:一条推文宣判了 Zig 的死刑
- Bun 是什么?为什么它举足轻重?
- 内存泄漏引发的连锁反应:Claude Code 的 14GB 噩梦
- Anthropic 收购 Bun:AI 驱动软件工程的基础设施
- 6天重写奇迹:从 Zig 到 Rust 的技术细节
- PORTING.md:一份 576 行的 AI 迁移指南
- Rust 版本的架构变化:Tagged Pointer 与 Trait 对象
- 性能对比:二进制体积、启动速度、内存占用
- 13,000 个 Unsafe 调用:代码质量的争议
- Zig 社区的抵制:No-AI Policy 与哲学决裂
- AI 重写软件的大趋势:Cloudflare、Ladybird 的案例
- 深度思考:未来开源可能禁止人类提交代码?
- 实战:如何从 Zig 迁移到 Rust——经验教训
- 总结与展望:软件工程的范式转移
1. 序幕:一条推文宣判了 Zig 的死刑
2026 年 5 月 11 日,Bun 创始人 Jarred Sumner 在 X 上发了一条推文:
"Bun v1.3.14 将于明日发布。如果我们合并 Rust 重写版本,这将是 Zig 的最后一个版本。"
就这么一句。
四年前,Bun 因为选择了 Zig 而显得特立独行——当全世界都在用 C++ 写运行时(Node.js)、用 Rust 写运行时(Deno)的时候,Bun 偏偏选中了这门还算小众的系统编程语言。
四年后,Zig 版本被它的创造者用一条推文宣告了终结。
这场从 Zig 到 Rust 的迁移,实际上只花了大约 六天,涉及 96 万行代码,并且在 Linux x64 glibc 环境下通过了现有测试套件的 99.8%。
而六天前,Jarred 还在 Hacker News 上说这是一堆根本还跑不起来的代码,"最后被全部扔掉的概率非常高"。
六天后,同样的代码变成了"Zig 的最后一个版本"。
这种速度,人类做不到。做到这一切的,是 Claude Code。
2. Bun 是什么?为什么它举足轻重?
2.1 Bun 的核心定位
Bun 是一个 All-in-One JavaScript 运行时,它的目标是从根本上替代 Node.js。与 Node.js 不同,Bun 不仅仅是一个运行时,它还包含了:
- 运行时:执行 JavaScript/TypeScript 代码(基于 JavaScriptCore,而非 V8)
- 包管理器:替代 npm/yarn/pnpm(
bun install比 npm 快 20-100 倍) - 打包器:替代 Webpack/esbuild(
bun bundle) - 测试框架:替代 Jest/Vitest(
bun test) - CLI 工具:直接运行 TypeScript 无需编译
2.2 为什么选择 Zig?
Bun 最初选择 Zig 作为实现语言,有几个关键原因:
- 性能可控:Zig 允许手动管理内存,没有垃圾回收的停顿
- 与 C 互操作方便:Zig 可以直接导入 C 头文件,调用 C 函数
- 编译时执行:Zig 的
comptime允许在编译期执行代码,生成高度优化的机器码 - 现代化工具链:Zig 自带跨平台交叉编译,一条命令编译到任何目标平台
// Zig 的 comptime 示例:编译期计算斐波那契数列
fn fibonacci(comptime n: u64) u64 {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
}
const result = comptime fibonacci(50); // 编译期计算,运行时零成本
2.3 Bun 的性能数据
Bun 官方宣称的性能数据(与 Node.js 对比):
| 操作 | Node.js | Bun | 加速比 |
|---|---|---|---|
| 启动时间 | ~45ms | ~3ms | 15x |
| 包安装 | ~30s | ~1s | 30x |
| 文件读取 | ~400μs | ~150μs | 2.7x |
| HTTP 服务 | ~15k req/s | ~80k req/s | 5.3x |
这些数据让 Bun 在 2023-2025 年间迅速崛起,成为 Node.js 最有潜力的挑战者。
3. 内存泄漏引发的连锁反应:Claude Code 的 14GB 噩梦
3.1 问题的根源
Bun 虽然性能优秀,但有一个长期困扰开发者的问题:内存泄漏。
2026 年 3 月 12 日,一个编号 #33453 的 Issue 被提交到 Claude Code 仓库:
"Claude Code 的主进程表现出严重的内存泄漏,RSS 内存在约 3 小时的短会话中从约 1.7GB 增长到 14GB 以上。泄漏位于 Bun 运行时的 WebKit Malloc 分配器中,而非用户空间的 JavaScript 分配。"
3.2 Claude Code 与 Bun 的紧密耦合
Claude Code 是以 Bun 可执行文件的形式发布的。当你安装 Claude Code 时,你实际上也在运行 Bun。
这并非简单的合作关系,而是紧密的依赖关系:
Claude Code (AI 编程助手)
↓ 基于
Bun Runtime (JavaScript 运行时)
↓ 使用
WebKit Malloc (内存分配器)
Anthropic Claude Code 负责人 Boris Cherney 曾解释:
"我们当初在开发 Claude Code 时,评估了很多运行时方案,Bun 几乎是毫无悬念的胜者。它的启动时间大概只有 3 毫秒,而 Python 要慢 15 倍左右。对于 CLI 工具来说,这意味着用户体验是'丝滑响应',还是'明显卡顿'。"
3.3 荒诞的循环
于是现在出现了一个非常荒诞的循环:
- Claude Code 被 Bun 的内存泄漏坑惨了
- Anthropic 让 Claude 去重写 Bun
- Bun 再继续回头支撑 Claude Code
甚至已经有开发者开始半开玩笑地担心:
"Bun 已经嵌入到 Claude Code 中。Claude Code 看起来糟透了。所以现在我担心 Bun 也可能糟透了。"
4. Anthropic 收购 Bun:AI 驱动软件工程的基础设施
4.1 收购背景
2025 年 12 月,Anthropic 收购了 Bun。官方说法是"加速 Claude Code 能力"。
本质上是要让 Bun 成为 Claude Code 背后的:
- 运行时(Runtime)
- 包管理器(Package Manager)
- 打包器(Bundler)
- 测试工具(Test Runner)
Anthropic 将 Bun 定义为 "AI 驱动软件工程的重要基础设施",并认为它能够帮助开发者以前所未有的速度构建和测试应用。
4.2 收购后的矛盾
收购完成后,一个深层的矛盾逐渐显现:
- Anthropic 是整个 AI Coding 浪潮最激进的推动者之一
- Claude Code 深度依赖 Bun runtime
- Zig 社区 全面封禁 AI 生成代码(No-AI Policy)
这种冲突,在 Anthropic 收购 Bun 后开始显得格外讽刺。
5. 6天重写奇迹:从 Zig 到 Rust 的技术细节
5.1 时间线
| 日期 | 事件 |
|---|---|
| 2026-05-07 | Jarred 发推:Rust 迁移涉及约 4000 次 commit、96 万行代码,只剩 3 个编译错误 |
| 2026-05-09 | Rust 版本通过 Bun 测试套件的 99.8% |
| 2026-05-11 | Jarred 发推:如果合并 Rust 版本,将是 Zig 的最后一个版本 |
| 2026-05-14 | Bun v1.3.14 发布,包含 Rust 重写版本 |
5.2 3天写代码,2天测试
Jarred 在推特上透露,整个过程大约是:
- 前 3 天:让 Claude 生成 Rust 代码
- 后 2 天:让 Claude 跑测试套件并修复失败用例
有开发者惊呼:"Claude 难道只用了三天就把 Zig 版 Bun 重写成 Rust 了吗?"
Jarred 回复说:"按代码量来看,这个说法准确。"
5.3 代码规模对比
| 指标 | Zig 版本 | Rust 版本 |
|---|---|---|
| 代码行数 | ~96 万行 | ~96 万行 |
| Commit 数量 | - | ~4000 次 |
| 测试通过率 | 基线 | 99.8% |
| 二进制体积 | 基线 | 缩小 3-8 MB |
6. PORTING.md:一份 576 行的 AI 迁移指南
6.1 文档结构
在 claude/phase-a-port 分支中,有一份长达 576 行的 PORTING.md 文档。这不是给人类看的,而是给 Claude Code 看的迁移指南。
文档把迁移拆成两个阶段:
Phase A:语义保真移植
- 逐文件忠实保留 Zig 的逻辑
- 即便 Rust 代码暂时不能编译也没关系
- 不允许使用
tokio/rayon/hyper/futures - 不允许
async fn(Bun 使用自己的事件循环) unsafe必须写明SAFETY注释- 遇到不确定逻辑时,宁可留下
TODO,也不要让 AI 自行猜测
Phase B:编译与优化
- 逐个 crate 解决编译问题
- 让测试套件通过
- 性能调优
6.2 为什么禁止 Async Fn?
Bun 使用自己实现的事件循环(基于 epoll/kqueue/IOCP),而不是 Rust 生态中常见的 tokio。
// ❌ 不允许这样(Phase A)
async fn read_file(path: &str) -> Result<Vec<u8>> {
// ...
}
// ✅ 必须这样(使用自己的事件循环)
fn read_file(path: &str, callback: impl Fn(Result<Vec<u8>>)) {
// 提交到 Bun 的事件循环
}
这种限制确保了 Rust 版本的行为与 Zig 版本高度一致。
7. Rust 版本的架构变化:Tagged Pointer 与 Trait 对象
7.1 Zig 的 Tagged Pointer 技巧
Zig 版本的 Bun 大量使用 Tagged Pointer 来优化性能:
// Zig 中的 Tagged Pointer 示例
// 利用指针的低比特位存储额外信息(因为指针对齐后低几位永远是 0)
fn Task(comptime T: type) type {
return struct {
// 指针的低 2 位存储任务类型
ptr: *T align(4),
fn init(ptr: *T, task_type: u2) @This() {
var self = @This(){
.ptr = ptr,
};
// 将 task_type 编码到指针的低比特位
self.ptr = @intToPtr(*T, @ptrToInt(ptr) | task_type);
return self;
}
fn get_type(self: @This()) u2 {
return @truncate(u2, @ptrToInt(self.ptr));
}
fn get_ptr(self: @This()) *T {
return @intToPtr(*T, @ptrToInt(self.ptr) & ~@as(usize, 0b11));
}
};
}
这种技巧在 Zig/C/C++ 中很常见,但在 Rust 中直接这样做会违反引用对齐规则。
7.2 Rust 中的替代方案
Jarred 在 X 上向 Rust 社区请教更底层的问题:
"Bun 原来的 Zig 代码大量使用 tagged pointer 来处理 event loop task、进程退出回调、非阻塞文件 I/O 等接口;迁到 Rust 后,如果直接用 trait 或函数指针,可能会带来额外开销。我还在寻找一种既不影响性能、又更适合 Rust 的实现方式。"
社区给出的建议包括:
- 使用
NonZero指针类型 + 手动编码标签 - 使用枚举 +
Box(会有额外分配) - 使用
unsafe手动操作指针(最接近原实现)
// Rust 中的 Tagged Pointer 实现(unsafe)
use std::ptr::NonNull;
struct TaggedPointer<T> {
ptr: NonNull<u8>, // 低 2 位存储标签
_marker: std::marker::PhantomData<T>,
}
impl<T> TaggedPointer<T> {
const TAG_MASK: usize = 0b11;
fn new(ptr: *mut T, tag: u2) -> Self {
let addr = (ptr as usize) | (tag as usize);
// SAFETY: 调用者必须确保 ptr 是对齐的(低 2 位为 0)
unsafe {
Self {
ptr: NonNull::new_unchecked(addr as *mut u8),
_marker: std::marker::PhantomData,
}
}
}
fn get_ptr(&self) -> *mut T {
// SAFETY: 低 2 位被掩码掉,恢复原始指针对齐
((self.ptr.as_ptr() as usize) & !Self::TAG_MASK) as *mut T
}
fn get_tag(&self) -> u2 {
(self.ptr.as_ptr() as usize) as u2 & Self::TAG_MASK
}
}
8. 性能对比:二进制体积、启动速度、内存占用
8.1 二进制体积
Rust 重写版本的最直接改进:二进制体积缩小 3-8 MB。
这主要是因为:
- Rust 的泛型在单态化时会产生多个副本,但 Bun 的场景中泛型使用较少
- Rust 的 LLVM 后端在 dead code elimination 上更激进
- Zig 版本中包含了大量调试符号和未优化的路径
8.2 启动速度
Bun 的核心优势之一是 极快的启动速度(~3ms)。
Rust 版本在启动速度上:
- Linux x64:与 Zig 版本持平(~3ms)
- macOS ARM:略有提升(~2.8ms)
- Windows x64:提升明显(从 ~8ms 降到 ~4ms)
8.3 内存占用
这是 Rust 版本改进最明显的地方:
| 场景 | Zig 版本 | Rust 版本 | 改进 |
|---|---|---|---|
| 空闲状态 | ~45MB | ~38MB | -16% |
执行 bun install | ~180MB | ~140MB | -22% |
| 长时间运行(3小时) | 持续增长 | 稳定 | 无泄漏 |
Rust 的所有权系统帮助捕获了 Zig 版本中的多个内存泄漏点。
9. 13,000 个 Unsafe 调用:代码质量的争议
9.1 Theo 的质疑
2026 年 5 月 12 日,Theo(t3.gg 创始人)在 X 上抛出了一组对比数据:
"uv 包含 35 万行 Rust 代码,以及 73 个 unsafe 调用。
Bun Rust 移植版已经有 68.1 万行 Rust 代码,并且有 超过 13,000 个 unsafe 调用。"
73 vs 13,000,差了接近 180 倍。
9.2 Jarred 的回应
Jarred 几乎立刻回应:
"今天已经下降了大约 2000。我预计它会稳定在 1 万左右,因为 Bun 的大部分内容都是用 C 和 C++ 编写的,这种情况不会改变。"
9.3 为什么 Bun 需要这么多 Unsafe?
平心而论,这种对比确实不完全公平:
- uv(Astral 的 Python 包管理器)是一个相对纯粹的 Rust 项目
- Bun 需要与大量底层 C/C++ 代码打交道:
- JavaScriptCore(WebKit 的 JS 引擎)
- 文件系统操作
- 网络 I/O(基于系统调用)
- 内存分配器(WebKit Malloc → 迁移到 jemalloc/mimalloc)
这些场景都绕不开 unsafe。
// Bun 中典型的 unsafe 使用场景
unsafe extern "C" {
// 调用 JavaScriptCore C API
fn JSValueMakeFromJSONString(
ctx: JSContextRef,
string: JSStringRef
) -> JSValueRef;
// 调用系统调用
fn kevent(
kq: c_int,
changelist: *const kevent,
nchanges: c_int,
eventlist: *mut kevent,
nevents: c_int,
timeout: *const timespec
) -> c_int;
}
// 在 Rust 中包装这些调用
impl JSCContext {
unsafe fn eval_json(&self, json: &str) -> Result<JSCValue> {
let js_string = JSStringCreateWithUTF8CString(json.as_ptr() as *const c_char);
let js_value = JSValueMakeFromJSONString(self.ctx, js_string);
// SAFETY: js_value 由 JSC 管理生命周期,我们通过 Rust 类型系统跟踪
Ok(JSCValue::from_ref(js_value))
}
}
9.4 社区的担忧
尽管 Jarred 的解释有道理,但网友们在意的不仅仅是数字。
开发者 Aashish Ranjan Singh 在 X 上写道:
"UV rust 是由真正的开发人员编写的,每一行代码都经过了审查。
Bun rust 由 Agents 编写,由 Agents 审核,并由 Agents 批准和合并。
完全在意料之中的结果。"
这句话精准地刺中了许多人的不安。
10. Zig 社区的抵制:No-AI Policy 与哲学决裂
10.1 Zig 的 No-AI Policy
Zig 社区有一个极其严格的规则:禁止 AI 生成 issue、PR 甚至评论。
Zig 基金会成员 Loris Cro 曾公开表示:
"大量 LLM 贡献只会制造'幻觉 PR'、'垃圾噪音'以及动辄上万行、根本无法维护的提交。"
10.2 Bun 与 Zig 社区的裂痕
Bun 团队此前其实已经 fork 过 Zig。他们曾宣称,通过在 macOS 与 Linux 上引入 LLVM 并行代码生成,debug 编译速度提升了四倍。
但这些优化始终无法 upstream 回 Zig 官方,其中一个关键原因,就是 Zig 社区的 No-AI Policy——而 Bun 团队已经开始大量使用 AI 辅助开发。
10.3 哲学决裂
这种冲突,在 Anthropic 收购 Bun 后开始显得格外讽刺:
- 一边是 Zig 社区 全面封禁 AI 生成代码
- 另一边是 Bun 团队 开始用 Claude agent 大规模把 Zig 本身迁移出 Zig
某种意义上,这已经不仅仅是一次语言切换,而更像是 两种软件工程哲学的正面碰撞。
11. AI 重写软件的大趋势:Cloudflare、Ladybird 的案例
11.1 Cloudflare 的 Next.js 重写
Cloudflare 曾在一周内借助 AI 重新实现了 Next.js API 的大部分能力。
他们的做法是:
- 用 AI 分析 Next.js 的源码,提取核心 API 语义
- 用 AI 生成 Cloudflare Workers 上的兼容实现
- 人工审查关键路径,AI 处理边界情况
结果:一周完成,代码质量"出乎意料的好"。
11.2 Ladybird 浏览器的 JavaScript 引擎迁移
Ladybird 浏览器(由前 Apple Safari 工程师开发)在 两周内 将自己的 JavaScript 引擎从 C++ 迁移到了 Rust。
项目负责人在博客中写道:
"如果靠人工慢慢迁移,可能需要 6-12 个月。但借助 AI 辅助,我们在两周内完成了核心迁移,并且测试通过率达到 97%。"
11.3 Jarred 的预言
Jarred 自己在 5 月 3 日发过一条推文:
"这种 pipeline,任何 VC 支持的 OSS 或者有大量 GitHub issues 的公司都能搭建。更普遍地说,它可以用于自动修复用户报告的 bug。Opus 4.7、4.6 甚至 4.5 都能轻松做到。"
他还更早的时候预言过:
"我预计开源软件会走向完全相反的方向——未来甚至可能变成'禁止人类贡献代码'。人类依然会负责讨论问题、决定优先级,但真正写代码、提交 PR、回复和处理反馈、完成实现的工作,最终都会由 LLM 来完成。"
Bun 的这次重写,正是这句话的第一次大规模公开演练。
12. 深度思考:未来开源可能禁止人类提交代码?
12.1 AI 的优势
AI 在软件重写/迁移场景中的优势是明显的:
- 速度:6 天 vs 6 个月
- 一致性:AI 不会疲劳,不会遗漏边界情况
- 覆盖率:96 万行代码,每一行都被处理过
- 测试驱动:AI 可以同时生成代码和测试
12.2 AI 的劣势
但 AI 的劣势同样明显:
- 缺少架构判断:AI 不知道什么时候该重构,什么时候该保持原样
- Unsafe 泛滥:如 Bun 案例中的 13,000 个 unsafe 调用
- 不可解释性:当 AI 生成的代码出现 bug,人类很难调试
- 依赖模型训练数据:如果训练数据不包含最新的语言特性,生成的代码可能过时
12.3 人类的新角色
如果 Jarred 的预言成真,"人类不得提交代码",那么人类开发者的新角色可能是:
- 架构师:决定"做什么"和"为什么做"
- 审查者:审查 AI 生成的代码的关键路径
- 调试者:当 AI 生成的代码出现诡异 bug 时介入
- 伦理把关者:确保代码不包含安全漏洞或恶意逻辑
13. 实战:如何从 Zig 迁移到 Rust——经验教训
13.1 准备工作
如果你也想尝试用 AI 辅助进行语言迁移,以下是 Bun 案例中的经验教训:
1. 编写详细的迁移指南
不要只是说"把它从 X 语言迁移到 Y 语言"。你需要:
- 逐文件指定迁移优先级
- 明确禁止使用的依赖(如 Bun 禁止 tokio)
- 指定代码风格和命名规范
- 规定
unsafe的使用场景和文档要求
2. 分阶段进行
Phase 1: 让 AI 生成能编译的代码(不必正确)
Phase 2: 让 AI 让代码正确(通过测试套件)
Phase 3: 人工审查关键路径
Phase 4: 性能调优
3. 保持测试套件不变
Bun 能够如此快速地完成迁移,一个重要原因是 测试套件非常完善。
Rust 版本必须通过 原有测试套件 的 99.8%。这意味着:
- 行为一致性有保证
- 回归问题能被快速发现
- AI 可以自由尝试,不怕破坏功能
13.2 技术陷阱
陷阱 1:所有权模型差异
Zig 是手动管理内存的,而 Rust 有所有权系统。直接翻译可能导致:
// Zig 代码(手动管理内存)
fn process(data: []u8) []u8 {
let result = allocate(sizeof(u8) * data.len);
// 调用者负责释放 result
return result;
}
// 直接翻译成 Rust(编译错误)
fn process(data: &[u8]) -> &[u8] {
let mut result = Vec::with_capacity(data.len());
// ...
&result // ❌ 返回指向局部变量的引用
}
正确做法:使用 Vec 或 Box 转移所有权。
陷阱 2:错误处理语义
Zig 使用 error union 类型,而 Rust 使用 Result:
// Zig 的错误处理
fn read_file(path: []const u8) ![]u8 {
const file = try std.fs.openFile(path, .{});
defer file.close();
return try file.readToEndAlloc(allocator, std.math.maxInt(usize));
}
// Rust 的等价实现
fn read_file(path: &str) -> Result<Vec<u8>, std::io::Error> {
let mut file = File::open(path)?;
let mut buffer = Vec::new();
file.read_to_end(&mut buffer)?;
Ok(buffer)
}
AI 在翻译时,需要理解两种错误处理模型的语义差异。
14. 总结与展望:软件工程的范式转移
14.1 Bun 案例的启示
Bun 的 6 天 Rust 重写,不仅仅是一次语言迁移,它揭示了几个重要趋势:
- AI 已经成为生产力工具:6 天、96 万行代码,这个数字背后是 AI 辅助开发的真实能力
- 质量 vs 速度的平衡:13,000 个 unsafe 调用提醒我们,速度的提升可能伴随质量的下降
- 开源社区的文化冲突:Zig 的 No-AI Policy vs Bun 的 AI-Driven Development
14.2 对开发者的建议
在这个 AI 重写软件的时代,作为开发者,你应该:
- 掌握多种语言:这样你才能判断 AI 生成的代码是否地道
- 深入理解系统:当 AI 生成的代码出现诡异 bug,你需要有能力调试
- 学会与 AI 协作:不是"让 AI 替你写代码",而是"让 AI 帮你加速"
14.3 未来展望
Jarred 的预言——"未来开源可能禁止人类提交代码"——或许不会完全成真,但它指向了一个可能的未来:
- 人类 负责架构设计、需求分析、用户体验
- AI 负责代码实现、测试生成、bug 修复
- 人机协作 成为软件开发的新范式
在这个未来中,Bun 的 6 天 Rust 重写,可能只是一个开始。
参考资源
- 6天、96万行Rust、直接合并?Claude Code被Bun的内存泄漏拖垮后,Bun让Claude亲手重写了自己
- Bun 官方博客:Rust 重写公告
- Claude Code 仓库 Issue #33453:内存泄漏问题
- Zig 官方文档:No-AI Policy
- Theo (t3.gg) 关于 Bun Rust 重写的评论
- Jarred Sumner 的 X 账号:重写过程实时更新
文章字数:约 9,800 字
本文撰写于 2026 年 6 月,基于公开信息和社区讨论。技术细节可能在 Bun 的后续版本中发生变化,请以官方文档为准。