11天、100万行代码、16.5万美元:AI 如何帮 Bun 完成从 Zig 到 Rust 的史诗级重写
背景:一次意料之外又情理之中的技术决策
2026年7月8日,Bun 创始人 Jarred Sumner 在其个人博客和 X(前 Twitter)上宣布了一个震动了整个前端和系统编程社区的消息:经过 11 天的高强度工作,Bun 的全部核心运行时已从 Zig 语言完全重写为 Rust,新版本(v1.4.0)通过完整测试套件并在各平台性能测试中持平或超越原有水平。
这不是一次普通的技术升级,也不是一次冲动的语言切换。它背后是一整套关于系统级软件的可靠性、AI 编程的边界、以及语言生态演进逻辑的深层思考。更重要的是,它以一种几乎教科书式的方式,展示了当 AI 编程工具被用到极致时,能在多大程度上压缩软件工程的时间成本。
让我们从这场重写的起点开始——Zig 语言本身的问题说起。
一、为什么是现在?Zig 的内存问题积重难返
要理解这次重写,首先需要了解 Bun 为什么会从 Zig 转向 Rust。Bun 是一个高性能的 JavaScript 运行时和工具链,与 Node.js 和 Deno 竞争。它的设计目标非常明确:用极致的性能替代 Node.js,用更好的开发体验替代 Deno。在 2024 年至 2025 年期间,Bun 凭借其 Zig 编写的核心,在启动速度、HTTP 服务器性能等关键指标上确实大幅领先于竞争对手。
但问题也随之而来。
1.1 Zig 的内存安全困境
Zig 是一门设计理念极为优雅的语言:没有隐藏的控制流、没有垃圾回收、没有泛型黑魔法,一切都是显式的。C++ 能做的 Zig 都能做,而且做得更干净。Andrew Kelley(Zig 的创造者)在 2016 年设计这门语言时的核心诉求就是:让系统程序员拥有对硬件的完全控制权,同时避免 C++ 的复杂性陷阱。
然而,这套设计哲学在实践中遇到了一个难以绕过的根本问题——内存安全。
Zig 选择了和 C/C++ 一样的路线:内存安全由程序员自己保证。它提供了 std.debug.safe 模式来进行运行时安全检查,但这会显著拖慢性能,与 Bun 追求极致性能的目标形成了根本矛盾。在生产级别的性能要求下,Bun 不得不关闭安全检查,而这就意味着:
// Zig 代码:看似简洁,但内存安全全靠程序员自觉
const std = @import("std");
fn processBuffer(buffer: []u8) []u8 {
// 如果传入的 buffer 来自外部,这里没有任何安全保护
// use-after-free、缓冲区溢出——全凭程序员保证
return buffer[0..@min(buffer.len, 1024)];
}
// 调用方如果这样写——崩溃
test "buffer overflow" {
var big_buffer: [1024]u8 = undefined;
var slice = processBuffer(&big_buffer);
// 如果 slice 被存储并在后续访问,use-after-free 风险极高
}
Rust 采用了完全不同的策略:通过**所有权系统(Ownership)和借用检查器(Borrow Checker)**在编译期强制保证内存安全,不需要运行时开销:
// Rust 代码:编译期就保证内存安全
fn process_buffer(buffer: &[u8]) -> &[u8] {
// 切片引用天然带长度信息,Rust 保证引用永远有效
&buffer[..buffer.len().min(1024)]
}
// 调用方如果尝试 use-after-free,编译直接报错
#[test]
fn buffer_overflow() {
let big_buffer = vec![0u8; 1024];
let slice = process_buffer(&big_buffer);
// Rust 的生命周期系统保证 slice 不会 outlive big_buffer
}
1.2 累积的技术债务
根据 Jarred Sumner 在博客中的描述,Bun 在 2024 年中后期遇到了一个令人沮丧的局面:团队花了大量时间修复与内存相关的 bug,这些 bug 在生产环境中偶发出现,定位困难,而且即使修复了也无法保证彻底根除。Zig 的内存模型虽然理论上可控,但在百万行级别的复杂系统代码中,人工保证内存安全变成了一场永无止境的消耗战。
更关键的是,这些问题不是某一个模块的问题,而是渗透到了整个技术栈的各个层面:
- V8 集成层:JSC、C++ 和 Zig 的 FFI 交互中,内存生命周期的管理极为复杂
- 原生模块系统:Bun 支持直接加载
.node风格的原生模块,过渡边界的内存管理是重灾区 - HTTP 服务器底层:高性能网络 I/O 中,短生命周期对象的大量分配和释放极易触发内存问题
在这种情况下,团队面临一个经典的工程决策:是继续在 Zig 路线上打补丁,还是换一个能从根本上解决问题的技术栈?
二、AI 驱动的重写:64 个 Claude 实例,11 天的奇迹
2.1 为什么选择 AI 而不是人工重写?
如果是传统的软件开发流程,将一个 100 万行代码的系统级项目从一门语言迁移到另一门语言,通常需要以下步骤:
- 需求冻结:完全冻结新功能开发
- 架构设计:设计 Rust 版本的架构(可能需要 2-4 周)
- 人员准备:招聘或培训 Rust 开发者(高水平系统程序员极为稀缺)
- 分阶段迁移:按模块逐个翻译,通常需要 1-2 年
- 质量保证:确保迁移后的行为与原版完全一致
对于 Bun 这样一个竞争激烈的项目来说,停止功能开发 1-2 年是不可接受的。性能领先优势会迅速被 Node.js 和 Deno 追赶,而开源社区的活跃度也会因为缺乏新功能而下降。
AI 编程工具提供了一个全新的可能性:在保持功能开发的同时,利用 AI 并行加速翻译工作。Jarred Sumner 的核心赌注是:AI 能否在代码转换层面做到足够好,使得"机械式翻译"成为可能?
答案是:不仅可能,而且超出了大多数人的预期。
2.2 翻译架构:64 个并行 Agent 的流水线
这次重写的执行方式本身就值得深入分析。Sumner 没有让一个 AI 实例来完成所有工作——那样的话 token 上下文窗口会溢出,翻译质量也会因为缺乏专注度而下降。相反,他设计了一套并行翻译流水线:
主控 Agent(Orchestrator)
├── Agent 群 1-16:核心运行时模块(I/O、文件系统、网络)
├── Agent 群 17-32:JavaScript 引擎集成(V8/JSC 绑定)
├── Agent 群 33-48:工具链模块(打包器、测试运行器、构建工具)
└── Agent 群 49-64:工具链周边与工具(CLI、调试接口)
每个 Agent 负责翻译一组紧密相关的源文件,并维护一个翻译状态共享文档,记录:
- 哪些 Zig 惯用法被转换为哪种 Rust 模式
- 复杂的类型映射关系(如 Zig 的
anytype→ Rust 的trait object或泛型) - FFI 边界的安全处理规则
这种架构的核心洞察是:并行 AI 翻译的关键不是速度,而是一致性。如果每个 Agent 独立工作而不共享翻译决策,最终会产生数十种不同的"方言",导致合并后的代码根本无法编译。
2.3 翻译策略:从逐行翻译到语义等价
一个有趣的问题是:Zig 和 Rust 在语法和语义层面并不完全等价,如何处理那些"没有直接对应"的语言特性?
策略一:语义等价替代
Zig 的某些特性(如 comptime 编译时执行)在 Rust 中没有直接对应。AI 的处理方式是找到功能上等价的 Rust 方案:
// Zig: 使用 comptime 进行编译时计算
const std = @import("std");
fn makeProcessor(comptime T: type) type {
return struct {
value: T,
fn process(self: *@This(), input: T) T {
return self.value + input;
}
};
}
const IntProcessor = makeProcessor(i32);
var proc = IntProcessor{ .value = 42 };
// Rust: 使用 const generics 和编译时求值
use std::marker::PhantomData;
struct Processor<T> {
value: T,
_marker: PhantomData<T>,
}
impl<T> Processor<T> where T: Copy + Add<Output = T> {
fn process(&self, input: T) -> T {
self.value + input
}
}
let proc = Processor::<i32> { value: 42, _marker: PhantomData };
策略二:Rust-idiomatic 改造
AI 在翻译过程中不仅仅做语言转换,还会应用 Rust 的最佳实践:
// Zig: 手动错误处理,错误作为返回值
const std = @import("std");
pub fn readFile(path: []const u8) ![]u8 {
const file = try std.fs.cwd().openFile(path, .{});
defer file.close();
const content = try file.readToEndAlloc(std.heap.page_allocator, std.math.maxInt(usize));
return content;
}
// Rust: 使用 ? 运算符和标准错误传播,同时引入 thiserror 增强可读性
use std::fs;
use std::path::Path;
use thiserror::Error;
#[derive(Error, Debug)]
pub enum FileError {
#[error("IO error: {0}")]
Io(#[from] std::io::Error),
}
pub fn read_file<P: AsRef<Path>>(path: P) -> Result<Vec<u8>, FileError> {
let content = fs::read(path)?; // ? 自动传播错误
Ok(content)
}
策略三:安全增强
这是 AI 翻译中最有价值的部分——AI 不仅翻译代码,还会在翻译过程中主动添加 Rust 的安全保证:
// Zig: 指针操作,没有任何边界检查
fn sliceBytes(data: []u8, start: usize, len: usize) []u8 {
return data[start..start + len]; // 如果越界,Zig 允许裸指针访问
}
// Rust: 编译期保证安全,并提供显式的 unsafe 边界
fn slice_bytes(data: &[u8], start: usize, len: usize) -> Option<&[u8]> {
// Rust 的 Option 类型强制调用方处理越界情况
start.checked_add(len).and_then(|end| {
if end <= data.len() {
Some(&data[start..end])
} else {
None
}
})
}
// 如果确实需要高性能路径,可以显式使用 unsafe
unsafe fn slice_bytes_unchecked(data: &[u8], start: usize, len: usize) -> &[u8] {
data.get_unchecked(start..start + len)
}
三、翻译过程中的关键技术挑战
3.1 错误处理的哲学差异
Zig 和 Rust 都有强大的错误处理机制,但设计哲学有显著差异。Zig 使用 !T 的"错误联合类型",而 Rust 使用 Result<T, E>。这两种方式在语义上几乎等价,但表达风格不同:
// Zig: 错误集是显式声明的类型
const FileError = error {
NotFound,
PermissionDenied,
Unexpected,
};
fn readConfig() FileError!struct { content: []u8, path: []u8 } {
// ...
}
// Rust: 错误可以用任何实现了 Error trait 的类型表示
use thiserror::Error;
#[derive(Error, Debug)]
pub enum ConfigError {
#[error("file not found: {0}")]
NotFound(String),
#[error("permission denied: {0}")]
PermissionDenied(String),
#[error("unexpected error: {0}")]
Unexpected(String),
}
fn read_config() -> Result<(Vec<u8>, PathBuf), ConfigError> {
// ...
}
AI 在处理这类差异时,采取的策略是优先保持语义等价,然后根据 Rust 的最佳实践进行风格优化。在大规模翻译中,这意味着需要建立一套翻译规则库,确保相同的 Zig 模式始终被翻译为相同的 Rust 模式。
3.2 异步模型的翻译
Bun 的高性能在很大程度上依赖于精细的异步 I/O 管理。Zig 的异步模型与 Rust 的 tokio 体系有本质区别:
// Zig: 内联异步,await 是语言内置的
const std = @import("std");
async fn fetchUrl(url: []const u8) ![]u8 {
const client = try std.http.Client.init(.{});
defer client.deinit();
const response = try client.fetch(.{ .location = .{ .url = url } });
return try response.body.readAllAlloc(std.heap.page_allocator, std.math.maxInt(usize));
}
// Rust: async/await + tokio 生态
use reqwest;
use anyhow::Result;
async fn fetch_url(url: &str) -> Result<Vec<u8>> {
let client = reqwest::Client::new();
let response = client.get(url).send().await?;
let body = response.bytes().await?;
Ok(body.to_vec())
}
翻译异步代码是最复杂的部分,因为 Rust 的 async/await 模型要求整个调用链都是 async 的,而 Zig 的 async 是可选择加入的。AI 需要识别哪些函数路径涉及 I/O,并确保整个链路上所有函数都正确标记为 async。
3.3 FFI 边界的处理
Bun 的核心价值之一是与 V8 JavaScript 引擎的深度集成,这涉及大量 C/C++ 和 Zig 的 FFI(外部函数接口)代码。Rust 在 FFI 方面有成熟的工具链(bindgen、cxx 等),但翻译过程中仍需要解决大量问题:
// Zig: 使用 @cImport 直接导入 C 头文件
const c = @cImport(@cInclude("v8.h"));
// Rust: 使用 bindgen 自动生成安全绑定
use v8::{Isolate, HandleScope, Local, Value};
// bindgen 会从 v8.h 自动生成 Rust 类型的 FFI 绑定
// 同时提供 Send + Sync 的安全约束(如果可能的话)
AI 在处理 FFI 翻译时,需要特别注意的是不要丢失安全性约束——原版 Zig 代码中许多 FFI 调用实际上是不安全的,但这个"不安全"是隐式的。翻译到 Rust 后,这些调用需要被显式地用 unsafe 块包裹,并且需要通过代码审查确保没有引入新的内存安全问题。
四、重写结果:数字背后的真相
4.1 翻译规模与质量
根据 CSDN 博客和 IT 之家等多个来源的综合报道,这次重写的规模如下:
| 指标 | 数值 |
|---|---|
| 总 Zig 文件数 | 1,448 个 |
| 生成的 Rust 文件总行数 | 748,921 行(cloc 统计) |
| 翻译耗时 | 11 个自然日 |
| 并行 Agent 数量 | 64 个 |
| 测试套件通过率 | 100%(最终版本) |
| 成本(API 费用) | 约 16.5 万美元 |
值得注意的是,100 万行是包含注释和空行的总代码量。实际的有效代码(cloc 统计)为 748,921 行,其中大约 40% 是测试代码。
4.2 性能表现
Sumner 在博客中公布的性能测试结果显示,Rust 版 Bun 在各个平台上均达到了或超越了原有 Zig 版的性能:
- Linux x64 (glibc):性能持平,部分 microbenchmark 提升 3-5%
- macOS (Apple Silicon):性能持平
- Windows:性能略有下降(约 2%),团队正在优化
二进制文件体积方面,Linux 版本从 Zig 版的约 45MB 缩小到了约 37-42MB(取决于构建配置)。这个体积减少主要来自 Rust 更好的链接优化和去除了一些 Zig 运行时开销。
4.3 稳定性提升
这是重写最被低估的成果。Bun 团队报告称,Rust 版上线后的 P0(最高优先级)崩溃报告数量下降了约 70%。这些崩溃主要来自之前提到的内存安全问题——use-after-free、空指针解引用、缓冲区溢出——在 Rust 的所有权系统下几乎被彻底根除。
五、深远影响:为什么这次重写不只是 Bun 的事
5.1 AI 编程的临界点
Bun 的案例向我们揭示了一个重要的事实:AI 编程工具的能力边界,正在快速逼近"完成复杂系统级软件工程"的标准。
在 2023-2024 年,AI 编程的主流叙事还是"AI 辅助写函数"、"AI 生成测试用例"、"AI 帮助调试"。这些场景的特点是任务边界清晰、上下文有限、输出可验证。
Bun 的重写将这个边界推到了一个全新的高度:
- 规模:100 万行级别的代码转换
- 复杂性:系统级软件,涉及内存管理、并发、FFI、编译器集成
- 质量要求:100% 测试通过、性能不降、行为完全一致
- 并行度:64 个 Agent 协调工作
这意味着 AI 编程工具已经具备了承担复杂软件工程任务的能力,而不仅仅是"辅助编写单个函数"。
5.2 语言生态的新动态
Bun 从 Zig 转向 Rust,也在语言社区引发了广泛讨论。作为 Zig 最大的生产级用户之一,Bun 的离开对 Zig 生态系统是一个打击。但 Andrew Kelley 本人在博客上的回应出人意料地平和:
"Bun 选择 Rust 是一个合理的技术决策。Zig 的内存安全保证需要显式启用,而这与极致性能目标存在张力。我祝愿 Sumner 和他的团队在 Rust 版上一切顺利。"
这段话展现了 Andrew Kelley 作为语言设计者的成熟:他不试图挽留用户,而是坦然承认 Zig 在某些场景下的局限性。
与此同时,Bun 的案例也在 Rust 社区引发了另一种讨论:Rust 是否已经准备好迎接大规模从其他系统语言迁移过来的项目?答案是基本准备好了,但还有一些工具链上的短板:
cxxcrate 已经非常成熟,适合 Rust-C++ 互操作bindgen在自动生成 C 头文件绑定方面已经很好用- 但 Zig 到 Rust 的自动翻译工具目前还不存在——Bun 的这次重写是完全人工设计流程、AI 执行翻译,而不是使用某个现成的迁移工具
5.3 对其他项目的启示
Bun 的案例给所有系统级软件项目提供了一个重要的参考框架:
什么情况下应该考虑语言迁移?
- 内存安全问题成为开发速度的瓶颈(持续投入但问题不减)
- 目标语言能从根本上解决核心痛点(而不是换一种方式表达同样的问题)
- 有足够的技术储备理解两种语言的差异(不能盲目相信 AI 翻译)
- 测试基础设施足够完善(没有 100% 测试覆盖率就不要做这种事)
什么情况下应该拒绝语言迁移?
- 当前语言能解决所有实际问题
- 项目规模小,内存安全问题的影响有限
- 测试覆盖率不足,无法保证迁移质量
- 团队对目标语言缺乏深入理解
六、技术细节:迁移过程中的 Rust 最佳实践
6.1 使用 thiserror 和 anyhow 增强错误处理
Bun 团队在迁移过程中广泛使用了 Rust 的两个错误处理库:thiserror 用于定义应用程序级别的错误类型,anyhow 用于处理需要传播错误的动态错误场景:
// thiserror:定义结构化错误类型,适合库代码
use thiserror::Error;
#[derive(Error, Debug)]
pub enum JsError {
#[error("TypeError: {message}")]
TypeError { message: String },
#[error("RangeError: {message}")]
RangeError { message: String },
#[error("ReferenceError: {variable} is not defined")]
ReferenceError { variable: String },
#[error("SyntaxError: {message}")]
SyntaxError { message: String },
}
// anyhow:处理动态错误,适合应用代码
use anyhow::{Context, Result};
pub fn load_module(path: &Path) -> Result<Module> {
let source = fs::read_to_string(path)
.with_context(|| format!("Failed to read module at {}", path.display()))?;
let ast = parse(&source)
.context("Failed to parse module")?;
Ok(Module::new(ast))
}
6.2 使用 tracing 进行结构化日志
高性能 I/O 系统的调试是一个巨大挑战。Bun 迁移到 Rust 后,采用了 tracing 库进行结构化日志记录:
use tracing::{info, warn, error, instrument};
use tracing_subscriber::{fmt, layer::SubscriberExt, util::SubscriberInitExt};
#[instrument(skip(buffer), fields(buffer.len = buffer.len()))]
pub fn process_request(buffer: &[u8]) -> Result<Response> {
if buffer.len() < 8 {
warn!(received_bytes = buffer.len(), "Request buffer too small");
return Err(JsError::RangeError {
message: "Buffer too small".into()
});
}
info!("Processing request: {} bytes", buffer.len());
// ... 处理逻辑
Ok(response)
}
tracing 的关键优势是它与 tokio 的集成:异步任务中的 instrument 属性会自动附加任务 ID,使得跨异步边界追踪请求变得极为简单。
6.3 性能敏感路径的 unsafe 策略
Rust 的 unsafe 不是"禁用安全检查",而是一种显式的信任契约。Bun 团队在处理性能敏感的热点路径时,采取了严格的 unsafe 使用策略:
// 高性能字节处理:使用 unsafe 但在注释中明确标注安全要求
pub fn fast_memcpy(dst: &mut [u8], src: &[u8]) {
assert_eq!(dst.len(), src.len(), "Buffer size mismatch");
// SAFETY: 上述 assert 确保两个切片不重叠且大小相等
// 这是 memcpy 的前置条件保证
unsafe {
std::ptr::copy_nonoverlapping(
src.as_ptr(),
dst.as_mut_ptr(),
src.len()
);
}
}
// 在安全层中封装 unsafe,形成安全的公共 API
pub fn copy_buffer(src: &[u8]) -> Vec<u8> {
let mut dst = vec![0u8; src.len()];
fast_memcpy(&mut dst, src);
dst
}
这种模式确保了 unsafe 代码被限制在最小的范围内,并且每个 unsafe 块都有清晰的文档说明其安全前提条件。
七、未来展望:从 Bun 到更广泛的 AI 工程实践
7.1 AI 翻译工具的进化方向
Bun 的案例揭示了一个巨大的市场需求:高质量的语言间代码翻译工具。目前市场上还没有成熟的 Zig → Rust 自动翻译工具,Bun 的成功很大程度上依赖于 Sumner 团队精心设计的翻译流程和人工监督。
可以预见,未来会出现专门针对系统语言迁移的 AI 工具,这些工具需要具备:
- 语义理解能力:不仅仅是语法转换,还要理解语言的语义等价物
- 一致性保证:确保翻译结果在整个代码库中风格一致
- 安全审计:自动识别翻译中可能引入的安全问题
- 增量翻译:支持在翻译过程中继续开发原版代码,然后增量翻译变更
7.2 Rust 在前端工具链中的地位巩固
Bun 的迁移进一步巩固了 Rust 作为前端基础设施首选语言的地位。在此之前,已经有多个重要的前端工具选择了 Rust:
- SWC:基于 Rust 的 JavaScript/TypeScript 编译器,被 Next.js、Vite 等广泛采用
- Rolldown:Rollup 的 Rust 版本,由 ByteDance 开发
- Biome:基于 Rust 的格式化工具,替代 ESLint + Prettier
- Dprint:基于 Rust 的格式化工具,性能远超 Prettier
Bun 的加入使得 Rust 在"JavaScript 运行时"这个关键领域也确立了自己的地位。现在,如果你要在 JavaScript 生态中构建一个高性能的基础设施组件,Rust 几乎是最自然的选择。
7.3 对软件开发范式的影响
这次重写最深远的影响可能在于它对软件开发范式的启示:AI 正在将"语言迁移"从一个需要数年规划的巨型工程,变成一个可以在数周内完成的可行选项。
如果这种趋势持续下去,未来可能出现以下场景:
- 项目在发现当前语言无法满足需求时,更快速地评估和执行语言切换
- AI 工具链成为语言学习的加速器——不需要精通目标语言也能完成翻译
- 语言选择变得更加务实——"用 AI + 目标语言重写"变成了一种被广泛接受的工程策略
总结
Bun 从 Zig 到 Rust 的 11 天重写,是 2026 年软件工程领域最具里程碑意义的事件之一。它不仅展示了 AI 编程工具在复杂工程任务中的能力边界,也揭示了 Rust 在系统级软件开发中越来越难以替代的价值。
这次重写的成功取决于几个关键要素:
- 完善的测试基础设施:没有 100% 的测试覆盖率,任何语言迁移都是赌博
- 团队的双语言能力:AI 翻译需要人工监督和纠正,不能盲目相信自动翻译
- 清晰的问题域理解:知道为什么要迁移,以及迁移能解决什么问题
- 良好的项目管理:64 个并行 Agent 需要精心设计的协调机制
对于国内开发者而言,这个案例同样值得深思。在追求新技术的同时,我们是否也应该反思:当前使用的技术栈是否存在根本性缺陷?AI 工具能否帮助我们更快地解决这些问题?
Rust 首次进入 TIOBE 编程语言排行榜前十(2026 年 7 月),以及 Bun 的这次重写,都在指向同一个方向:内存安全正在成为系统级软件开发的首要考量,而 Rust 是目前在这方面做得最好的主流语言。
最后,值得一提的是,AI 编程工具在这个过程中扮演的角色:它不是替代人类工程师,而是放大人类工程师的生产力。Sumner 团队中真正决定"这段代码应该这样翻译而不是那样翻译"的,始终是人类开发者。AI 负责的是执行,而人类负责的是决策。这个分工在可预见的未来内,应该不会有根本性的改变。
至少——在真正的 AGI 到来之前是这样。
本文数据来源:Jarred Sumner 官方博客(2026年7月8日)、CSDN 技术博客、IT之家、腾讯云开发者社区等。性能测试数据来源于 Bun 团队官方 benchmarks。