Bun 的创建者 Jarred Sumner 宣布,Bun 这款 JavaScript/TypeScript 运行时、打包器和包管理器,已经完成从 Zig 到 Rust 的重写。此次迁移涉及约 53.5 万行代码,原本预计需要一年,但在 AI 辅助下,项目用了四个月完成。
这并不只是一次语言替换。对运行时、编译器和打包工具而言,内存生命周期贯穿解析、缓存、并发执行和原生扩展等多个边界。重写的核心目标,是借助 Rust 的所有权系统和借用检查器,减少反复出现的内存安全漏洞与泄漏问题。
为什么内存安全会成为重写动因
Zig 允许开发者显式管理内存,并提供了比较清晰的底层控制能力。但这种控制也意味着,团队需要持续维护分配器、释放路径、错误处理和跨线程数据访问之间的约束。
在一个大型 JavaScript 运行时中,典型风险包括:
- 一个对象在仍被使用时提前释放,形成 use-after-free。
- 错误路径遗漏释放操作,导致内存泄漏。
- 多个组件对同一块内存的所有权理解不一致。
- 缓存、异步任务和原生 API 之间存在生命周期错配。
Rust 的借用检查器不能自动修复所有逻辑错误,但它能把大量“谁负责释放、这个引用能活多久、多个线程能否同时访问”的约束前移到编译阶段。对于运行时这类长期运行、并发密集且资源敏感的程序,这种约束具有直接价值。
AI 加速了迁移,但没有取消工程约束
从一年压缩到四个月,AI 辅助是这次迁移中最引人注目的部分。大规模重写中,AI 可以帮助生成重复性的类型转换、API 适配、测试迁移和编译错误修复建议。
不过,AI 更擅长处理局部转换,较难独立判断以下问题:
- 新旧实现是否保持相同的边界行为。
- 某个生命周期设计是否适合整个模块,而不只是让当前函数通过编译。
- 性能回退来自算法、分配策略,还是锁竞争。
- 失败路径是否覆盖了真实生产环境中的资源清理。
因此,AI 让“写出能编译的 Rust”更快,却不会自动提供正确的架构。迁移仍然需要基准测试、回归测试、模糊测试以及对内存占用和吞吐量的持续观测。
一个最小的 Rust 所有权示例
下面的程序可以直接运行,展示 Rust 如何阻止一个已经转移所有权的值被继续使用。它不是 Bun 的实现代码,而是理解这类重写动机的最小示例。
将内容保存为 src/main.rs,并执行命令:
cargo new ownership-demo
cd ownership-demo
# 用下面的代码替换 src/main.rs
cargo run
fn consume_buffer(buffer: Vec<u8>) -> usize {
buffer.len()
}
fn main() {
let payload = vec![1_u8, 2, 3, 4];
let size = consume_buffer(payload);
println!("payload size: {size}");
// println!("{}", payload.len());
// 取消上一行注释后,编译器会报告 payload 已经被移动。
}
payload 被传入 consume_buffer 后,所有权转移给函数。调用方不能继续使用原变量,因此不会悄悄保留一个已经失效的引用。实际工程通常会根据需求选择借用、可变借用、引用计数或显式克隆,而不是把所有数据都复制一遍。
例如,只读处理可以改为借用:
fn inspect_buffer(buffer: &[u8]) -> usize {
buffer.len()
}
fn main() {
let payload = vec![1_u8, 2, 3, 4];
let size = inspect_buffer(&payload);
println!("payload size: {size}");
println!("first byte: {}", payload[0]);
}
这种设计把生命周期关系写进类型和函数签名:函数只临时读取数据,调用方仍然拥有 payload。在大型代码库中,这类约束会逐渐扩展到缓存、解析树、任务队列和原生资源管理。
迁移项目应如何验证结果
类似 Bun 这样的重写,不能只用“编译通过”作为完成标准。更可靠的验证清单包括:
- 对比新旧实现的 CLI 行为、错误信息和退出码。
- 使用相同输入运行打包、解析和模块加载基准测试。
- 检查峰值内存、长期运行时的 RSS 和分配次数。
- 对解析器、模块解析和文件系统边界进行模糊测试。
- 在 CI 中保留 Sanitizer、并发测试和回归样例。
- 观察真实工作负载中的启动时间、吞吐量和尾延迟。
Rust 可以降低一类内存安全问题的发生概率,但它不会阻止逻辑泄漏、无限缓存、错误的资源上限或不合理的并发模型。迁移后的系统仍需要明确的性能预算和运行时监控。
给团队的实践建议
如果团队正在考虑类似的跨语言重写,可以把项目拆成几个可验证的阶段:先锁定外部行为和性能基线,再迁移边界清晰的模块,随后逐步替换共享数据结构和资源管理层。每个阶段都应保留旧实现与新实现的对照测试。
AI 适合承担机械、重复、反馈周期短的工作,例如生成初版转换代码和整理编译错误;架构决策、并发模型、错误语义和性能验收仍应由熟悉系统的工程师负责。
Bun 的案例说明,语言重写的价值不只在于更换语法。真正值得评估的是:新语言是否能把高频缺陷转化为编译期约束,团队是否有足够测试支撑行为一致性,以及迁移后的性能和维护成本是否符合产品目标。