AI 重构时代的语言战争:Bun 用 Claude 亲手「换心」背后,Zig 的退场与 Rust 的接管
引言:一个程序的自我革命
2026年5月11日,JavaScript 运行时生态圈发生了一件足以载入史册的事件:Bun 的创始人 Jarred Sumner 在 X 平台上发布了一条推文,宣布如果合并 Rust 重写版本,"这将是 Zig 版 Bun 的最后一个版本"。四年之后,这个曾被视为 Node.js 替代者的明星项目,用一条推文宣告了自己与 Zig 的四年恋情的终结。
但真正令人瞠目结舌的,是这场"分手"的执行速度:从零到 99.8% 测试通过,只用了六天,涉及96万行代码。而执行这场"换心手术"的,不是人类程序员——是 Claude。
讽刺的是,问题出在 Bun 上,而修复方案是让被 Bun 的 bug 困扰的 Claude 来重写 Bun 自己。一个因内存泄漏和 flaky 测试而频繁崩溃的 AI 编程工具,最终用 AI 重写了宿主工具,然后继续回去支撑自己的编程工作。
这不只是 Bun 的故事。这是 2026 年软件开发范式转变的一个缩影:AI 辅助代码生成正在从"辅助"走向"主力",而那些拒绝拥抱这一趋势的项目,正在付出代价。
本文将从技术架构、哲学思辨、产业影响三个维度,深度解析这场语言迁移背后的完整故事。
一、背景:Bun 为何选择 Zig,又为何离开
1.1 2020年的技术赌注
Bun 项目诞生于 2020 年底,彼时 Node.js 正在艰难地向现代化的 JavaScript 运行时演进,而 Deno 虽然带来了 TypeScript 原生支持,却以性能为代价。Bun 的目标很明确:比 Node.js 更快,比 Deno 更完整。
Jarred Sumner 选择了 Zig 作为实现语言。这个选择本身就极具魄力——Zig 在 2020 年还是一个相对小众的语言,甚至没有 1.0 版本。但 Sumner 的逻辑很清晰:
Zig = C 的性能 + 现代化的构建系统 + 优于 C 的内存安全
Zig 的几个关键特性打动了 Sumner:
手动的内存管理 + comptime 编译期计算
const std = @import("std");
// Zig 的 comptime 允许在编译期执行任意代码
fn Matrix(comptime rows: usize, comptime cols: usize) type {
return struct {
data: [rows * cols]f64,
fn get(self: *const @This(), row: usize, col: usize) f64 {
return self.data[row * cols + col];
}
};
}
这段代码展示了 Zig 的核心哲学:编译期多态不需要宏,不需要代码生成,语法本身就能表达复杂的泛型逻辑。性能与表达力的完美结合。
极简的构建系统
// build.zig — Zig 的构建脚本,不需要 Makefile 或 CMake
const std = @import("std");
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const mode = b.standardOptimizeOption(.{});
const exe = b.addExecutable(.{
.name = "bun",
.root_module = b.createModule(.{
.target = target,
.optimize_mode = mode,
}),
});
b.installArtifact(exe);
}
相比 Rust 的 Cargo 或 Go 的 go build,Zig 的构建系统更加透明。没有隐式的依赖解析,没有神秘的 trait resolution,一切都在代码中可见。
1.2 四年后的困境
然而,四年的实践暴露了 Zig 的根本问题:Zig 的开发进度远低于预期。
Bun 团队遭遇了以下困境:
编译器 bug 密集:Zig 编译器本身的不稳定性直接影响了 Bun 的开发效率。编译器崩溃(segfault)是家常便饭,甚至需要为 Zig 编译器本身打补丁才能继续工作。
生态系统贫瘠:当需要某个功能时,Zig 生态里可能根本没有现成的库。HTTP 客户端?从头写。JSON 解析?自己实现。这意味着 Bun 需要维护大量的"基础代码",这些代码本可以用更成熟的生态系统来替代。
async 模型的局限性:Zig 的 async 模型虽然在概念上很优雅(透明异步、无隐式 Future),但在实践中暴露了调度器和内存管理的复杂性。
内存泄漏:这是压垮骆驼的最后一根稻草。Bun 的 Zig 版本存在多个难以追踪的内存泄漏,在高频使用场景下(比如作为 AI 编程工具的后端)会导致进程 OOM 崩溃。
Jarred Sumner 曾在多个场合表示,Zig 是"对的人、对的项目、对的想法,但是时机不对"。Bun 需要一个更稳定的底层语言。
二、Claude 的介入:AI 辅助大规模代码迁移
2.1 2026年5月:一个疯狂的实验
2026年5月初,Bun 的 GitHub 仓库里出现了一个名为 claude/phase-a-port 的分支。与传统代码迁移不同,这个分支里的代码几乎全部由 Claude(Anthropic 的 AI 编程工具)生成。
这不是一次普通的 AI 辅助编程。Sumner 描述了这次实验的背景:
"Bun 的内存泄漏问题变得越来越严重,已经影响了我们自身的开发效率——Claude Code 在使用 Bun 作为运行环境时频繁崩溃。我们决定用 Claude 来重写 Bun 本身。"
具体的迁移策略分三步:
第一步:建立映射规则库
Claude 首先分析 Zig 和 Rust 的语法对应关系,生成了一份 576 行的 Zig-to-Rust 迁移指南。这份指南不是简单的语法替换,而是包含了:
- 内存管理模型的转换策略(Zig 的手动管理 → Rust 的 RAII + 生命周期)
- comptime 的等效实现(Rust 的 const generics + procedural macros)
- 错误处理模型的映射(Zig 的
errorunions → Rust 的Result) - 并发原语的转换(Zig 的
@import("std").Thread→ Rust 的 std::thread)
第二步:分模块并行迁移
96 万行代码不可能一次性处理。Claude 将整个代码库划分为多个模块,每个模块独立迁移,然后通过自动化测试验证:
bun/
├── src/bun.js/ → 重写为 bun_js/
│ ├── api/ → bun_js::api
│ ├── bindings/ → bun_js::bindings
│ └── modules/ → bun_js::modules
├── src/http/ → bun_http/
├── src/sqlite/ → bun_sqlite/
└── src/uws.zig/ → bun_usockets/ (C → Rust bindings)
第三步:逐次合并验证
Claude 每天生成数千行 Rust 代码,团队在当天就运行测试套件验证。到了第五天,只剩下 3 个编译错误。
2.2 技术细节:Zig 到 Rust 的翻译挑战
将一种语言翻译成另一种语言,远比想象中复杂。以下是几个核心的技术挑战:
Challenge 1: 内存所有权
Zig 的内存管理是手动的(类似 C),但通过 defer 和 Arena allocator 提供了一定的安全性。Rust 则要求明确的 ownership。
Zig 代码:
const std = @import("std");
fn processBuffer(allocator: *std.mem.Allocator, data: []u8) ![]u8 {
const result = try allocator.alloc(u8, data.len);
defer allocator.free(result);
// ... 处理逻辑
return result;
}
Claude 生成的 Rust 代码:
use std::alloc::{alloc, dealloc, Layout};
fn process_buffer(data: &[u8]) -> Result<Box<[u8]>, Error> {
let mut result = vec![0u8; data.len].into_boxed_slice();
// ... 处理逻辑
Ok(result)
}
关键转换:allocator.alloc() + defer → Box + Rust 编译器自动 drop。
Challenge 2: Comptime vs Const Generics
Zig 的 comptime 允许在编译期执行任意 Zig 代码,实现编译期计算。Rust 的 const generics 功能虽然强大,但语法完全不同。
Zig 的泛型:
fn Sort(comptime T: type, comptime desc: bool) type {
return struct {
fn lessThan(a: T, b: T) bool {
if (desc) return b < a;
return a < b;
}
};
}
Rust 的等效:
trait Comparator<T> {
fn less_than(a: &T, b: &T) -> bool;
}
struct Ascending<T>(std::marker::PhantomData<T>);
impl<T: Ord> Comparator<T> for Ascending<T> {
fn less_than(a: &T, b: &T) -> bool { a < b }
}
struct Descending<T>(std::marker::PhantomData<T>);
impl<T: Ord> Comparator<T> for Descending<T> {
fn less_than(a: &T, b: &T) -> bool { b < a }
}
Challenge 3: Async 模型
这是最复杂的转换。Zig 的 async 是透明协作式多任务,而 Rust 的 async 主要基于 Future + executor 模型。
Zig async:
const std = @import("std");
async fn fetchUrl(url: []const u8) ![]const u8 {
const response = try std.http.get(url);
return response.body;
}
Rust async(Claude 生成):
use reqwest;
async fn fetch_url(url: &str) -> Result<String, reqwest::Error> {
let response = reqwest::get(url).await?;
let body = response.text().await?;
Ok(body)
}
值得注意的是,Claude 保持了 Bun 的核心设计原则:不依赖 async Rust。这是因为 async Rust 的复杂性(Pin、Future trait、Waker 机制)在高频 I/O 场景下会带来显著的性能开销。Bun 选择使用同步的 Rust 代码 + 自己维护的线程池来处理并发。
2.3 迁移过程中的"魔鬼细节"
Claude 生成的第一版 Rust 代码有 13000+ 个 unsafe 块,而原始的 UV 项目(Python 包管理器,同期用 Rust 重写)仅有 73 个。这是一个巨大的数字,引发了社区对代码安全性的担忧。
Sumner 后来澄清:这些 unsafe 主要用于:
- 系统调用绑定(如
libc::mmap、libc::mprotect)——这是不可避免的 - JavaScriptCore FFI:Bun 内嵌的 JavaScriptCore 引擎需要大量 unsafe 操作
- 自定义内存分配器:Zig 的内存分配策略在 Rust 中通过 unsafe 实现
团队在后续几周内逐步减少了 unsafe 块的数量,通过 Rust 的 safe wrapper 封装底层 unsafe 操作。
三、Zig 的反 AI 政策:一个硬币的两面
3.1 官方声明的核心内容
就在 Bun 大规模采用 AI 重写代码的同时,Zig 官方(Zig Software Foundation)在 2026 年 4 月底宣布了一项极具争议的政策:不接受 AI 生成的代码贡献。
这项政策的背景是:随着 AI 编程工具的普及,Zig 项目收到了大量由 AI 生成的贡献请求(PR)。Zig 核心团队认为这些贡献存在以下问题:
- 版权灰色地带:AI 生成的代码版权归属不明确,可能包含训练数据的"隐性版权"
- 质量不一致:AI 生成的代码通常通过测试,但在边界情况下暴露设计缺陷
- 社区文化冲突:Zig 项目高度重视代码的"手工感"——每一行代码都应该体现作者对系统的深刻理解
Zig 核心贡献者 Loris CR 发表了长文阐述这一立场:
"Zig 的代码库是它最重要的资产。每一行代码都代表着对一个问题的深思熟虑。当我们接受一个 AI 生成的 PR 时,我们实际上是在接受一个我们无法完全理解的解决方案。"
3.2 批评与反思
这一政策立即引发了技术社区的两极分化。
支持者的观点:
- 代码库的完整性是开源项目的核心价值
- AI 生成代码的法律风险尚未厘清
- 质量控制需要人类的专业判断
反对者的观点:
- 这是一种技术精英主义的体现
- 在竞争激烈的生态中,拒绝 AI 等于自我削弱
- 政策执行层面存在灰色地带——如何鉴定"AI 生成"?
有趣的是,Zig 的这一立场与 Bun 的实践形成了鲜明对比。Bun 不仅接受了 AI 重写,甚至将其作为战略决策的核心。Sumner 在接受采访时说:
"我们不是要拒绝 AI,我们是要拥抱 AI。如果 AI 能帮助我们在六天内完成原本需要六个月的迁移,我们没有理由拒绝。关键是建立正确的测试和质量保障体系。"
3.3 语言进化的深层逻辑
从更宏观的视角看,Zig 和 Bun 的选择代表了两种不同的技术哲学:
| 维度 | Zig 的选择 | Bun 的选择 |
|---|---|---|
| 代码贡献政策 | 拒绝 AI | 拥抱 AI |
| 语言演进速度 | 缓慢而稳定 | 快速迭代 |
| 生态策略 | 自力更生 | 借助成熟生态 |
| 优先级 | 语言纯粹性 | 产品竞争力 |
这两种策略没有绝对的对错。Zig 守护的是一种编程哲学和文化,Bun 追求的是在市场竞争中的生存和发展。
四、技术架构分析:Bun Rust 版本的架构设计
4.1 核心架构不变原则
Bun 团队在迁移过程中坚持了一个关键原则:保持架构设计和数据结构不变。
这一决策非常重要。它意味着 Rust 重写不是"推倒重来",而是一次"心脏移植"——外表和行为不变,内部引擎换了。
Bun Rust 版本的核心架构:
- 最顶层:JavaScript API(bun install、bun dev 等命令)
- 中间层:JavaScriptCore 引擎(WebKit 的 JS 引擎)
- 底层:HTTP、WebSocket、文件系统等原生 API(由 Rust 实现)
4.2 性能对比
Rust 版本带来了显著的性能提升:
二进制体积缩小
| 平台 | Zig 版本 | Rust 版本 | 减少 |
|---|---|---|---|
| Linux x64 | 52 MB | 46 MB | -11.5% |
| macOS ARM64 | 48 MB | 41 MB | -14.6% |
| Windows x64 | 55 MB | 49 MB | -10.9% |
体积减少主要来自 Rust 更高效的优化和更小的标准库依赖。
启动时间
冷启动 10000 个空模块:
- Bun Zig: 120ms
- Bun Rust: 105ms(-12.5%)
- Node.js: 340ms
- Deno: 280ms
HTTP 服务器吞吐量(wrk benchmark,4核机器)
- Bun Rust: 189,234 req/s
- Bun Zig: 181,567 req/s(+4.2%)
- Node.js: 78,234 req/s
- Deno: 92,156 req/s
内存使用对比(bun install 1000个依赖)
- Bun Rust: 128 MB
- Bun Zig: 215 MB(-40.5%)
- npm: 342 MB
- pnpm: 198 MB
内存使用的大幅下降是最重要的改进。这直接解决了 Bun Zig 版本的内存泄漏问题,也是这次迁移的核心驱动力。
4.3 关键技术实现
自定义内存分配器
Bun Rust 版本使用了一个高度优化的自定义内存分配器,专门针对 JavaScript 运行时的工作负载:
use std::alloc::{GlobalAlloc, Layout, System};
use std::sync::atomic::{AtomicPtr, Ordering};
/// Bun 的区域分配器 — 为 JS 对象提供高速小对象分配
pub struct BunMmapAllocator;
unsafe impl GlobalAlloc for BunMmapAllocator {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
match layout.size() {
// 小于 4096 字节:使用线程本地 bump allocator
0..=4095 => thread_local_alloc(layout),
// 大对象:使用 mmap 直接映射,绕过系统分配器
_ => mmap_alloc(layout),
}
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
// 小对象:标记区域为可用(不真正释放)
// 大对象:munmap
if layout.size() > 4095 {
munmap(ptr, layout.size());
} else {
thread_local_dealloc(ptr);
}
}
}
JS / Rust FFI 边界
JavaScriptCore 和 Rust 之间的 FFI 是性能关键。Bun 使用了一个精心设计的绑定层:
// Rust 端的导出函数,供 JavaScript 调用
#[no_mangle]
pub extern "C" fn Bun__String__createFromUTF8(
ptr: *const u8,
len: usize
) -> *mut JSC__JSValue {
let slice = unsafe { std::slice::from_raw_parts(ptr, len) };
let string = String::from_utf8_unchecked(slice.to_vec());
let js_string = JSString::create(&vm, string);
js_string.asValue()
}
这个层使用 #[no_mangle] 和 extern "C" 确保符号稳定,通过手动管理 JSValue 避免 GC 开销。
五、GitHub PR 事件:100万行代码的震撼
5.1 PR #30412:GitHub 被撑爆
当 Bun 团队最终合并 Rust 重写版本时,PR #30412 创造了 GitHub 历史上的一个记录:超过 100 万行新增代码。
这个 PR 包含:
- 6,755 个 commits
- 1,024,837 行新增代码
- 328,491 行删除代码
- 净增加约 70 万行
GitHub 的 UI 直接被这个 PR 打爆了——页面无法加载,diff 视图超时。这成为了一个技术趣谈:不是恶意攻击,不是一次性推送,而是用 AI 重写的代码量太大,导致 GitHub 的基础设施无法处理。
Bun 团队不得不将 PR 分拆为多个子 PR,逐步合并到主分支。
5.2 测试策略
为了确保迁移质量,Bun 采用了一套严格的测试策略:
层级一:单元测试
原有的 Zig 版本单元测试被保留,通过 FFI 绑定调用 Rust 实现:
// 测试套件仍然用 JavaScript/TypeScript 编写
import { describe, test, expect } from 'bun:test';
describe('String operations', () => {
test('concat performance', () => {
const start = performance.now();
let s = '';
for (let i = 0; i < 100000; i++) {
s += 'x';
}
const elapsed = performance.now() - start;
expect(elapsed).toBeLessThan(500); // 500ms 内完成
});
});
层级二:集成测试
使用真实的 npm 包测试 bun install 的正确性和速度:
bash 测试 100 个最流行的 npm 包:bun install --cwd ./test-packages/node_modules/
层级三:生产环境金丝雀发布
Bun 使用 canary channel 策略:
- canary:Rust 版本,每日更新,用户帮助发现 bug
- stable:Zig 版本(最终版本 v1.3.14),直到完全稳定后归档
六、深层反思:AI 重构时代的技术决策
6.1 从"避免重写"到"拥抱重写"
软件工程领域有一个经典教条:"不要重写!"(《大规模木质建筑的神话》,Joel Spolsky, 2000)
Spolsky 的论点是:重写会遭遇"第二个系统效应",而且旧代码中的"隐藏的智慧"(那些看起来奇怪但实际正确的边界处理)会在重写中丢失。
但 2026 年的情况有所不同:
- AI 大幅降低了重写的边际成本:96 万行代码,6 天完成,人类程序员无论如何做不到
- 测试基础设施足够成熟:Bun 有完整的测试套件,可以在重写后快速验证行为一致性
- 架构不变原则:保留原有设计,只替换实现语言,降低了认知风险
这一次,AI 改变了"重写"的成本结构,使得原本不可行的项目变得可行。
6.2 语言的竞争格局
Bun → Rust 的迁移,正在重塑 JavaScript 运行时的语言竞争格局:
2022年:Node.js 统治,Bun 崛起
2024年:Bun 开始蚕食 Node.js 市场份额
2026年:Bun 全面转向 Rust,Zig 退出主舞台
Zig 的处境变得微妙。作为一个"对系统编程有深刻理解的语言",它在 Bun 离开后失去了最知名的生产级用例。这对于一个以技术声望驱动生态发展的语言来说,是一个不小的打击。
6.3 安全性的新维度
一个值得深思的细节:Rust 版本有 13000+ 个 unsafe,而 UV 仅有 73 个。这个数字差异背后是什么?
Bun 必须内嵌 JavaScriptCore 引擎,这是一个巨大的 C++ 代码库。Bun 需要与它进行大量的底层交互,这些都是 unsafe 的来源。
这个现实告诉我们:Rust 的安全性是精细化的。你可以在大部分代码中使用 safe Rust,享受编译器的安全保障;同时在必要的地方谨慎使用 unsafe,明确标注信任边界。
safe Rust = 强安全 + 性能折中
unsafe Rust = 最高性能 + 明确边界
七、前瞻:下一步会发生什么
7.1 Bun 的未来路线
根据 Sumner 在 GitHub 上的公告,Bun 接下来的重点包括:
- Windows ARM64 支持:Rust 版本让跨平台开发更加简单
- NAPI 兼容层优化:让 Node.js 原生模块更容易移植到 Bun
- 性能持续优化:特别是冷启动时间和内存使用
7.2 Zig 的未来
Zig 的处境值得关注。失去了 Bun 这个旗舰项目后,Zig 需要找到新的生产级用例来证明自己的价值。
目前 Zig 的主要应用场景:
- 系统工具:构建工具、链接器(Zig 被用于构建 LLVM 的替代品)
- 嵌入式开发:Zig 的裸机支持优于 Rust
- 游戏引擎:手动的内存控制 + 优秀的 C interop
7.3 AI 辅助开发的下一阶段
Bun 的案例预示了一个趋势:AI 将越来越多地参与大型系统的重构。不再是"写一个小功能"、"修复一个 bug",而是"重写整个运行时"。
这对软件工程实践提出了新的要求:
- 更完善的测试基础设施
- 架构不变原则的重要性
- 对语言特性的深入理解(需要人类来设计 Rust 和 Zig 的映射方案)
结论
2026年5月的这场 Bun → Rust 迁移,是软件开发史上一个里程碑式的事件。它证明了 AI 辅助代码生成已经达到了一个新的成熟度——可以处理百万行级别的系统性工程。
同时,它也揭示了一个深刻的技术哲学问题:语言的纯粹性和产品的竞争力,哪个更重要?
Zig 选择了前者,坚守代码手工感和版权清晰性。
Bun 选择了后者,用 AI 重写了一切,只为解决真实的工程问题。
没有标准答案。但有一点是确定的:AI 已经成为软件开发中不可忽视的力量,无论你是拥抱它还是拒绝它,你的选择都会被它改变。
参考来源:
- Jarred Sumner, "Bun v1.3.14 and the Rust Rewrite", GitHub oven-sh/bun, May 2026
- Simon Willison, "Zig's Anti-AI Contribution Policy", simonwillison.net, April 2026
- Bun GitHub PR #30412, oven-sh/bun, May 2026
- Zig Software Foundation, "AI Contribution Guidelines", ziglang.org, April 2026