编程 Bun 引擎换血:Zig 放弃与 Rust 重写全记录——11天、53万行代码、64个AI Agent、16.5万美元,一次改变JS生态认知的激进实验

2026-07-29 08:47:15 +0800 CST views 7

Bun 引擎换血:Zig 放弃与 Rust 重写全记录——11天、53万行代码、64个AI Agent、16.5万美元,一次改变JS生态认知的激进实验

前言:当创造者亲手埋葬自己的选择

2026年5月11日,一条来自 Bun 创始人 Jarred Sumner 的推文,在整个技术社区引发了地震。

"Bun v1.3.14 将于明日发布。如果我们合并 Rust 重写版本,这将是 Zig 的最后一个版本。"

就这样,四年前因选择 Zig 而被开发者社区视为"有态度"的技术决策,在四年后被它的创造者用一行文字宣告了死刑。

这不是一次普通的版本迭代,而是 JavaScript 运行时历史上一次前所未有的实验:53万行 Zig 代码,借助 Claude Fable 5(Anthropic 出品的 AI 编程模型),在 11 天内被全部重写为 Rust——零测试被删除、测试全部通过、代码量减少 20%、运行时提速 5%

这一事件的深远意义远不止于"一个项目换了个语言":它是 AI 编程能力边界的一次真实压力测试,是系统级编程语言演进的一个标志性节点,更揭示了未来软件开发流程中"人类决策者 + AI 执行者"这一协作模式的工程可能性。

本文将完整还原这场技术迁移的来龙去脉,深入剖析其架构决策背后的工程逻辑,并从程序员的视角,对这一事件给整个 JS 运行时生态带来的启示进行冷静评估。


一、Bun 的诞生与 Zig 之选:为什么当初不是 Rust

1.1 当时的语境:Node.js 的困境与 Deno 的刺激

2019 年,当 Jarred Sumner 开始构建 Bun 时,Node.js 生态正面临严重的性能焦虑。npm 的包膨胀、频繁的冷启动延迟、以及 C++ addons 的维护噩梦,让许多开发者对"JavaScript 全栈"产生了深深的倦怠感。

Ryan Dahl 在 Node.js 十周年之际公开反思了当初的设计错误,并转身推出了 Deno——一个试图用 Rust 改写一切的新运行时。但 Deno 的激进路线(完全放弃 npm、完全重写标准库)在生态兼容性上付出了沉重代价。

Jarred Sumner 选择了一条更务实的路:不是从零构建一个兼容层极薄的新生态,而是用更快的底层语言,完整地承接 Node.js 的 API 契约,同时把包管理、打包、测试等工具链一并集成到一个二进制文件里。

1.2 Zig 为什么会成为选择

Zig 是一门以"简洁、透明、零隐藏控制流"为设计哲学的系统语言,由 Andrew Kelley 于 2016 年创建。它的核心吸引力在于:

  • 无隐藏内存分配:所有内存分配都是显式的,不存在 GC,运行时行为完全可预测
  • 与 C 的零成本互操作:可以透明地链接任何 C 库,不需要 wrapper 或 FFI 绑定层
  • 编译期计算(comptime):类似 C++ constexpr,但语法更简洁、更安全
  • 没有 hidden control flow:错误处理通过 error uniontry/catch 显式表达
  • 编译错误而非运行时错误:许多在其他语言中会导致 panic 的情况,在 Zig 中是编译期错误

对于一个需要精确控制内存布局、无 GC 延迟、同时还要频繁调用 WebKit/JavaScriptCore 的运行时来说,这些特性几乎是为 Bun 量身定制的。

更重要的是,Zig 的学习曲线远低于 Rust。对于一个只有一个人的项目(Jarred Sumner 很长时间内是 Bun 的唯一全职开发者),Rust 的所有权系统和生命周期检查会极大地拖慢初期迭代速度。而 Zig 的上手门槛,让 Jarred Sumner 能够在相对短的时间内,从零构建出一个功能完整的运行时原型。

以下是 Bun 早期架构的核心组件,以及它们在 Zig 下的实现思路:

// Bun 的 HTTP Server 实现(Zig 版本,简化示例)
const std = @import("std");
const bun = @import("bun");

// 零拷贝请求解析,利用 Zig 的 comptime 避免反射开销
pub fn parseRequest(buffer: []const u8) !Request {
    const lines = std.mem.split(u8, buffer, "\r\n");
    const request_line = lines.next() orelse return error.InvalidRequest;
    
    var parts = std.mem.split(u8, request_line, " ");
    const method = parts.next() orelse return error.InvalidMethod;
    const path = parts.next() orelse return error.InvalidPath;
    
    return Request{
        .method = try std.meta.stringToEnum(Method, method),
        .path = path,
        .headers = try parseHeaders(lines),
    };
}

// Zig 的 async task 系统 —— 无栈协程,避免了 Go 的 GMP 模型复杂度
pub fn handleRequest(req: Request) !void {
    const route = try router.match(req.path);
    try route.handler(req);
}

// Bun 的 JavaScript 互操作层 —— Zig 透明调用 JavaScriptCore C API
pub fn toJSValue(comptime T: type, value: T) JSC.JSValue {
    return switch (@typeInfo(T)) {
        .Int, .Float => JSC.JSValueMakeNumber(ctx, @as(f64, value)),
        .Struct => // 递归处理结构体字段
        else => unreachable,
    };
}

1.3 但问题也随之而来

然而,随着 Bun 的代码库从几万行增长到 50 万行,Zig 的局限性开始暴露:

1. Zig 语言本身的稳定性不足。 Zig 的标准库 API 在 0.11 到 0.12 版本间经历了大量 breaking changes。由于 Bun 深度依赖 Zig 的 comptime 机制,每次 Zig 升级都可能引发连锁的编译错误。这对一个需要维护大型代码库的项目来说是致命的。

2. Zig 的工具链生态不够成熟。 Rust 有 Cargo、crates.io、rust-analyzer 等一整套经过大量项目验证的工具链。Zig 的包管理器(zigmod)在 2024 年才趋于稳定,IDEA/VSCode 的 Zig 插件支持也远不如 Rust 的 rls/rust-analyzer 完善。

3. Zig 的人才池极小。 当 Bun 被 Anthropic 收购并深度集成到 Claude Code 中后,Anthropic 的工程师们(大多有 Rust 背景)面对 50 万行 Zig 代码,需要额外的学习成本。

4. Zig 的错误处理在大规模代码库中的可维护性问题。 Zig 的 !T error union 类型要求每个调用点显式处理错误。当代码量达到 50 万行时,这导致大量的 trycatch 散布在代码各处,而 Rust 的 Result<T, E> + ? 运算符提供了更优雅的链式组合能力。


二、Anthropic 收购 Bun:Claude Code 依赖 Bun 的真实代价

2.1 内存泄漏:Claude Code 最大的工程债务

2026年3月,GitHub 上出现了编号 #33453 的 Issue:

"Claude Code's main process exhibits severe memory leaks, with RSS memory growing from ~1.7GB to over 14GB in ~3 hour sessions."

问题的根源指向了 Bun 本身的内存泄漏——泄漏发生在 Bun 运行时使用的 WebKit Malloc 分配器中,而非用户空间的 JavaScript 分配。

另一个更夸张的 Issue 记录了运行 14 小时后的状态:Claude Code 进程占用 23GB 虚拟内存,143.8% CPU,系统完全卡死。

2.2 Bun 的路线图争议

Reddit 用户 Xtergo 曾汇总了开发者对 Bun 的核心不满:

  1. 内存泄漏长期未修复:尽管 Bun 团队在 v1.1.13 中更换了内存分配器并声称"内存占用下降 5%",但社区普遍认为这只是杯水车薪
  2. GitHub Issue 积压严重:截至 2026 年 4 月,Bun 仓库有超过 3700 个 open issues
  3. 功能迭代优先于稳定性修复:社区感知到 Bun 在不断添加新功能,但底层稳定性的投入不足

三、Rust 重写:576行迁移圣经与64个并行 Agent

3.1 PORTING.md:一份被整个行业忽视的技术文档

当 Jarred Sumner 在内部开始试验性 Rust 迁移时,他做的第一件事不是写代码,而是写文档。

这份名为 PORTING.md 的文件长达 576 行,详细规定了 Zig → Rust 迁移的所有工程约束。

这份文档的核心理念是:AI 负责忠实的语义翻译,人类负责架构决策和性能优化

3.2 迁移的数学

指标数值
迁移耗时~11天
代码行数~53万行 Zig → ~42万行 Rust(减重20%)
并行 Claude Agent 数量最多64个同时工作
AI 模型Claude Fable 5
资金投入16.5万美元
测试套件通过率99.8%(Linux x64 glibc)

为什么是 64 个并行 Agent? 不同的 53 万行代码之间存在大量可以并行翻译的独立模块(HTTP parser、file system watcher、module resolver、bundler、test runner……)。Claude Fable 5 支持并发会话管理,使得 Bun 团队可以将代码库拆分成多个独立任务,用多个 Agent 并行处理。

3.3 tagged pointer:迁移中最棘手的工程问题

Jarred Sumner 在 X 上向 Rust 社区请教过最核心的技术问题:Bun 的事件循环大量使用 tagged pointer(一种将类型信息编码到指针低位的技巧):

// Rust 的 tagged pointer 实现(基于社区建议)
#[repr(C)]
struct Task {
    // 用 NonZeroUsize 表示"永不为零的指针",允许在同字段中编码额外信息
    tagged_ptr: std::num::NonZeroUsize,
}

impl Task {
    const TAG_MASK: usize = 0b111;
    
    fn new(ptr: *const u8, tag: u8) -> Self {
        debug_assert!(tag < 8);
        let addr = ptr as usize;
        debug_assert!(addr & Self::TAG_MASK == 0, "ptr must be aligned");
        Self {
            tagged_ptr: unsafe { 
                std::num::NonZeroUsize::new_unchecked(addr | (tag as usize))
            }
        }
    }
    
    fn get_ptr(&self) -> *const u8 {
        (self.tagged_ptr.get() & !Self::TAG_MASK) as *const u8
    }
    
    fn get_tag(&self) -> u8 {
        (self.tagged_ptr.get() & Self::TAG_MASK) as u8
    }
}

Unsafe 代码被严格限制在 new_unchecked 函数内,并标注了 SAFETY 约束。


四、工程流水线:如何让 AI 生成的代码通过生产级测试

4.1 从"三天写完代码"到"两天跑通测试"

最令人印象深刻的工程数据是:从 96 万行 Rust 代码出现在分支,到 Linux x64 glibc 测试套件通过 99.8%,只用了两天。

这个"两天"背后是一套精心设计的验证流水线:

第一步:逐文件语义验证
每个翻译后的 Rust 文件,都需要与原始 Zig 文件进行结构对比(行数、函数签名、类型数量),确保没有遗漏逻辑。

第二步:Rust 编译器类型检查
Phase A 完成后,Claude 逐个 crate 修复编译错误。由于 Phase A 的约束(保持 Zig 逻辑不变),编译错误大多是类型不兼容问题。

第三步:测试套件对抗性运行
Bun 的测试套件包含超过 15,000 个测试用例,涵盖 JavaScript 语法解析、HTTP 服务器、文件系统 API、npm 兼容层、bundler 功能等。

关键洞察:Phase A 的"不优化"策略

如果让 AI 在翻译阶段就做"Rust 风格优化",会导致两个问题:

  1. 无法对比测试:无法判断是"翻译错误"还是"优化引入的 Bug"
  2. 调试成本爆炸:如果 Rust 版本出现行为差异,无法快速定位问题

先让代码跑通,再优化——这是大模型辅助代码迁移的正确工程顺序。

4.2 99.8% 通过率意味着什么

剩余 0.2% 的失败测试,主要集中在:

  1. 平台特定行为:macOS ARM64 和 Windows 环境下的边缘情况(如路径处理、信号处理)
  2. 已知的技术债务:少数测试用例触发了 Rust 版本中的 TODO 注释

五、性能对比:Zig vs Rust 实战数据

5.1 基础 benchmark

测试场景Zig 版本Rust 版本差异
HTTP Hello World (req/s)185,000194,000+5%
npm install (典型项目)8.2s7.9s+4%
bun build (Bundle 1000 模块)1.1s1.05s+5%
冷启动时间3ms3ms持平
WASM 编译 (bench.wasm)0.42s0.40s+5%
内存占用 (idle)28MB27MB-4%
内存泄漏 (24h 连续运行)严重未检测到

5.2 性能提升的来源分析

Rust 版本提速 5% 的核心来源,不是算法改进,而是编译器优化质量

Rust 使用 LLVM 作为编译器后端,而 LLVM 在 x86-64 优化方面积累了超过 20 年的工程投入。相比之下,Zig 的后端优化器虽然也在持续改进,但在向量化和循环优化方面,与 LLVM 仍有差距。

5.3 Claude Code 的实测改善

Simon Willison(知名技术博主和 LLM 应用研究者)在 2026 年 7 月 19 日的博客中指出:

"Claude Code v2.1.181 在 Linux 平台上的启动速度比之前版本快了约 10%。分析可执行文件后发现,Claude Code 已经整合了 Rust 重构版的 Bun。"


六、技术哲学思考:语言选择是战略还是战术

6.1 Zig vs Rust:不是非此即彼的选择

这次迁移最值得玩味的,是 Jarred Sumner 对 Zig 的态度。他并没有说 Zig 是"坏语言",而是说:

"我真的很厌倦为内存泄漏、崩溃和稳定性问题而担忧和花费大量时间进行修复。如果编程语言能提供更强大的工具来预防这些问题,那就太好了。"

语言的选择,在项目的不同生命周期阶段,需要不同的权衡:

  • 初创期(0→1):开发速度和迭代灵活性是第一优先级。Zig 的低门槛和透明控制流让 Jarred Sumner 能够在极短时间内构建出功能完整的原型。这是正确的选择。
  • 增长期(1→10):需要吸引贡献者、扩大社区。此时 Zig 的人才池过小开始成为瓶颈。
  • 生产期(10→100):长期维护成本、稳定性和生态系统成熟度成为核心考量。Rust 在这些维度上的优势变得不可忽视。

6.2 AI 辅助编程的工程化意义

这次迁移真正值得深入思考的,不是"Rust vs Zig",而是**"AI 生成代码 + 人类工程决策"这一协作模式的可行性验证**。

PORTING.md 的 Phase A/Phase B 策略、逐文件对比验证、测试套件对抗性运行……这些都是人类工程师的工程决策。AI 的贡献是:将人类工程师的决策,以极快的速度忠实地执行出来。

AI 编程工具的价值,不在于它能代替程序员思考,而在于它能将程序员的工程决策,以 10 倍的速度变成代码。


七、现状与展望

7.1 Rust 版本的生产状态

截至 2026 年 7 月底,Bun 的 Rust 版本已经:

  • 在 Linux x86-64 glibc 环境下稳定运行,测试通过率 99.8%+
  • 被 Claude Code v2.1.181+ 集成使用
  • 正在积极推进 macOS ARM64 和 Windows 平台的适配
  • 保留了原有的 JavaScriptCore 引擎(未做替换)

7.2 对 JavaScript 运行时格局的影响

Node.js (C++)     → Chrome V8 引擎,老牌霸主,生态最完整
Deno (Rust)       → TypeScript 原生支持,安全性优先,生态追赶中
Bun (Rust)        → JavaScriptCore 引擎,速度最快,工具链集成最强
WinterCG (C++)    → 标准化的 Web API 实现,多运行时协作标准

Rust 现在同时驱动了两个主流 JS 运行时(Deno 和 Bun)。


八、写给程序员的话:从中可以学到什么

8.1 语言只是工具,架构才是壁垒

Bun 的核心价值,从来不是"用 Zig 写的",而是:

  • 与 Node.js API 的完整兼容性(超过 90% 的 npm 包开箱即用)
  • 将运行时、包管理器、打包器、测试框架集成到单一二进制中的设计哲学
  • 对 JavaScriptCore 引擎的深度优化

这些是架构决策,不是语言选择。

8.2 勇于推翻自己的选择,但要有数据支撑

Jarred Sumner 在 2022 年的采访中曾说"Zig 是最好的选择"。2026 年他亲手推翻了这一判断。

推动这个改变的不是情绪,而是数据:

  • 50 万行 Zig 代码的维护成本
  • 3700+ 个未关闭的 issues
  • Claude Code 用户因内存泄漏产生的负面反馈
  • Rust 生态(Anthropic 内部)的协同效率优势

8.3 AI 是放大器,不是替代品

PORTING.md 是人类工程师写的。
哪些代码可以并行翻译、哪些必须串行处理,是人类工程师决定的。
tagged pointer 的 Rust 移植方案,是人类工程师向社区请教后确定的。
测试套件的质量标准,是人类工程师制定的。

AI 做的,是忠实地、迅速地将这些决策变成代码。


结语

Bun 从 Zig 到 Rust 的迁移,是 2026 年技术圈最具启示性的工程事件之一。

它让我们看到了 AI 辅助编程在真实大型项目中落地的可能性,也让我们重新审视了系统编程语言选择的生命周期维度,更让我们思考:当代码的规模超过某个阈值之后,什么样的工具和组织方式才是可持续的。

最终,这个故事最打动我的,不是 11 天、53 万行、16.5 万美元这些数字本身,而是 Jarred Sumner 在整个过程中展现出的工程诚实:

他承认了自己的错误判断,
设计了严格的工程约束来保证 AI 的输出质量,
在社区中公开讨论技术选型的权衡,
用数据而非情绪来做决策。

这才是这个故事真正值得每一个程序员记住的地方。


标签:Bun | Rust | Zig | JavaScript运行时 | AI编程 | Claude Code | Anthropic | 代码迁移 | 系统编程 | 性能优化

关键词:Bun | Rust重写 | Zig迁移 | JavaScriptCore | AI Agent | Claude Fable | 代码重构 | 运行时性能 | tagged pointer | PORTING.md | 技术债务 | Anthropic | 编程语言选型

推荐文章

MySQL 优化利剑 EXPLAIN
2024-11-19 00:43:21 +0800 CST
mysql关于在使用中的解决方法
2024-11-18 10:18:16 +0800 CST
windon安装beego框架记录
2024-11-19 09:55:33 +0800 CST
Web 端 Office 文件预览工具库
2024-11-18 22:19:16 +0800 CST
php 统一接受回调的方案
2024-11-19 03:21:07 +0800 CST
淘宝npm镜像使用方法
2024-11-18 23:50:48 +0800 CST
Linux查看系统配置常用命令
2024-11-17 18:20:42 +0800 CST
程序员茄子在线接单