news 2026/8/1 5:44:49

Bun 用 11 天把 53 万行 Zig 迁到 Rust:一次 AI 主导的大规模语言迁移工程拆解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Bun 用 11 天把 53 万行 Zig 迁到 Rust:一次 AI 主导的大规模语言迁移工程拆解

2026 年 7 月 8 日,JavaScript 运行时 Bun 的创始人 Jarred Sumner 在官方博客发布了一篇震动技术圈的声明:经过 11 天的高强度工作,Bun 全部 Zig 代码被机械式转换为 Rust,新版 v1.4.0 已进入 canary 通道。这不是一次普通的版本更新,而是一场把“预计需要一个小团队一整年”的语言级重写压缩到不到两周的工程实验,主导者只有一名工程师,外加预发布版 Claude Fable 5。

这件事之所以值得前端与基础设施开发者认真拆解,不在于“AI 又干了件大事”的猎奇,而在于它把两个长期困扰系统级软件的问题同时摆上了台面:第一,当一个混用“垃圾回收”与“手动内存管理”的运行时膨胀到 53 万行时,内存安全 bug 为什么会从偶发变成结构性顽疾;第二,当重写的规模大到任何人工团队都无法在合理时间内完成时,怎样用一套可验证的工程流程让百万行 LLM 生成代码“敢于合并”。

本文不重复新闻通稿,而是依据 Bun 官方博客、GitHub 仓库与多方媒体报道,拆解这次迁移背后的设计决策:为什么是 Rust 而非 C++/Go、机械移植策略为何能成立、对抗式审查循环如何堵住 LLM 代码的质量漏洞,以及迁移后的实测收益与尚未解决的局限。

一、53 万行 Zig 的稳定性困境:为什么必须换语言

Bun 诞生于 2021 年 4 月,Jarred Sumner 最初只是把 esbuild 的 Go 代码逐行移植成 Zig,看中的是 Zig 极简的语法与零开销抽象。凭借一站式集成(JS/TS/CSS 转译打包、npm 兼容包管理器、Jest 风格测试运行器、完整 Node.js 模块解析、HTTP/WebSocket 客户端),Bun CLI 月下载量已突破 2200 万,Vercel、Railway、DigitalOcean 原生适配,Claude Code、OpenCode 等工具将其作为默认运行时。

但规模本身就是问题。据官方博客披露,不含注释的 Zig 代码已达 535,496 行(中文媒体报道总文件数 1448 个、含注释与空行约 78 万行)。Jarred 在博客中直接贴出 v1.3.14 修复的一批典型高危 bug,根因高度一致——都是“手动管理内存”与“JS 垃圾回收”混用时的生命周期失控:

  • node:zlib异步.write()仍在进行时调用.reset(),触发 heap-use-after-free;
  • node:http2重入回调(如session.request()写在 timeout listener 里)引发哈希表 rehash,使内部流指针失效;
  • UDPSocket.send()中用户valueOf()/toString()回调在捕获负载与实际发送之间分离了 ArrayBuffer;
  • crypto.scrypt输出缓冲分配失败时,回调与受保护的口令/盐缓冲永不释放,造成泄漏;
  • CSS 解析器在background-clip带厂商前缀且多层背景时双重释放;
  • tlsSocket.setSession()每次调用泄漏一个 SSL_SESSION(约 6.5 KB)。

这些不是孤立失误。Jarred 坦言,团队已经叠加了远超多数项目的防御手段:给 Zig 编译器打补丁加入 Address Sanitizer、每次提交都跑 ASAN、Windows 上发行 ReleaseSafe 构建、用 V8/JavaScriptCore 同款 fuzzer Fuzzilli 7×24 小时模糊测试、外加大量端到端泄漏测试。但模糊测试发生在代码合并之后、CI 发生在推送之后、ASAN 发生在运行时——所有反馈都太晚,只能在 bug 出现后被动修补。

问题的结构性根源在于语言本身。Zig 像C一样不替你管理内存,没有构造/析构函数,清理工作要在每个调用点用defer/errdefer显式写出。官方博客用一张对照表点明了三种语言的清理机制差异:

Language Cleanup mechanism Zig defer, errdefer (显式,靠人记得写) C++ ~Destructor, &&Move (隐式析构,但靠规范约束) Rust Drop (隐式,编译器强制)

当一个*T被传给许多函数时,到底哪一处负责释放?哪些函数在调用后还要引用这块内存?Bun 现有的做法是“arena 生命周期 + 引用计数 + 极度仔细”的混合体。Jarred 指出,理论上可以用风格指南(如 TigerBeetle 的 TigerStyle、Google 三万字的 C++ 规范)来约束,但风格指南的软肋是“靠代码审查尽力执行”。而 Zig 没有运算符重载,强行引入自研智能指针会让代码退化成下面这种难看的形态:

fn foo(a_ptr: SharedPtr(TCPSocket)) !void { const a: *TCPSocket = a_ptr.get(); defer a_ptr.deref(); const b = try do_something_with_a(a); defer b.deref(); // ... }

对比原本符合 Zig 直觉的写法,自研智能指针牺牲了人体工学,却换不来 Rust 那样的编译期保证。这正是团队最终放弃“在 Zig 里加智能指针”路线、转向整体迁移的根本原因。

二、为什么是 Rust 而非 C++ 或 Go

在选定 Rust 前,团队认真评估过 C/C++ 与 Go。Bun 约 20% 的代码本就是 C++,还内嵌了 JavaScriptCore、uWebSockets、lshpack/lsquic、BoringSSL、SQLite 等 C/C++ 库——切到 C++ 能删掉大量extern "C"桥接代码、也能拿到析构函数。但 C++ 依旧依赖“风格指南 + 代码审查”来防内存错误,即便有 ASAN,内存损坏与泄漏仍会发生。

Rust 的吸引力可以用一句话概括,这也是官方博客的核心论点:在 safe Rust 中,那一长串 use-after-free、double-free、“错误路径上忘记释放”全部变成编译错误,并通过Droptrait 实现 RAII 式自动清理。编译错误是比风格指南更好的反馈回路——它把“内存有且仅释放一次”这件事从人的注意力转移到了类型系统。

值得注意的是,这与同期 TypeScript 7.0 选 Go 而非 Rust 的逻辑形成对照。TypeScript 负责人 Ryan Cavanaugh 曾解释:Rust 的所有权模型禁止没有重大变通的循环数据结构,而 TypeScript AST 充满循环引用,用 Rust 需重新设计编译器数据模型,那是数年工作且无法保证兼容;Go 移植只用了约一年并保持语义一致。两件事说明同一规律:选语言不是选“最强”的,而是选“约束恰好匹配痛点”的——Bun 的痛点是内存生命周期,Rust 的 Drop 正中靶心;TS 的痛点是并行与原生速度且 AST 大量循环,Go 的 goroutine 与宽松所有权更合适。

三、机械移植策略:让测试套件成为迁移的“锚”

语言级重写在软件工程里素来是高风险决策。Jarred 自己也承认“历史上重写通常是个糟糕的主意”。降低风险的唯一现实路径,是做一次行为零改动的机械移植(mechanical port):不重构架构、不动业务逻辑、不改性能特性,只把 Zig 逐文件翻译成 Rust,并用同一套测试套件验证。

这条策略能成立,关键前提是一个常被忽视的工程决策——Bun 的测试套件用 TypeScript 编写,与运行时的实现语言解耦。这意味着迁移 Rust 后,百万级断言的测试可以原样跑在新生成的二进制上,测试本身不需要被重写。媒体披露的细节进一步印证了移植的“机械”程度:约 1448 个 Zig 文件被转成 100 个 Rust crate(子仓库),期间要攻克超过 16000 个编译错误与循环依赖问题,并产出PORTING.mdLIFETIMES.tsv等迁移文档供后续贡献者参照。

这种“先机械翻译、再渐进重构”的两段式策略,值得任何考虑大规模语言迁移的团队借鉴。官方博客明确:先让 Rust 版“看起来像从 Zig 转译出来的”,等 v1.4 上线后再逐步减少unsafe用法、向地道 Rust 靠拢。把“功能等价”与“代码地道”拆成两个独立阶段,避免了既要保兼容又要写漂亮代码的双重风险。

四、对抗式审查循环:让百万行 LLM 代码“敢于合并”

11 天、百万行新增代码、单人主导——这类 PR 最大的挑战不是“写出来”,而是“怎么建立合并的信心”。官方博客把方法论讲得很透,核心是一个被显式建模的“写代码—审查”循环:

// 官方博客给出的伪代码,非真实代码 let task; while ((task = todoList.pop())) { const result = task(); const feedback = await Promise.all([review(result), review(result)]); await apply(feedback, result); }

Jarred 用约 50 个 Claude Code 动态工作流持续跑了 11 天,每个工作流都是上面这样的循环,分别负责生成迁移指南、逐文件机械移植、修复每个 crate 的编译错误、让bun test/bun build跑通、让全部测试通过、以及多轮大型重构与清理。

真正有意思的是“对抗式审查”(adversarial review)的隔离设计。它的灵感来自人类工程实践:写代码的人想合并,会有确认偏误;审查的人只找问题。于是 Claude 也被拆成两个独立上下文——1 个实现者配 2 个以上对抗审查者,实现者不审查、审查者不实现。审查者拿到的是纯净的 diff,并被要求“假设这段代码是错的”,穷尽地找出 bug 与失效理由。

官方博客晒出了对抗审查在合并前真实捕获的三个 bug,且每个都带 commit 哈希作为佐证。这三个 bug 全部能编译通过、看起来都合理,却藏着致命缺陷:

Bug 1:异步 close 的 use-after-free + double-free(commitf0a454376c7)。代码把Box<uv::Pipe>交给 libuv 异步关闭,但Box在 match 分支结束时就被 drop 了,libuv 还持着已释放的指针,回调时又释放一次:

for stdio in [spawned_stdout, spawned_stderr] { match stdio { StdioResult::Buffer(mut pipe) => { // pipe: Box<uv::Pipe> — hand it to libuv to close pipe.close(Subprocess::on_pipe_close) } StdioResult::Fd(fd) => fd.close(), StdioResult::Unavailable => {} } } // 修复:用 Box::leak 让所有权转移给 libuv,避免提前 drop // Box::leak(pipe).close(Subprocess::on_pipe_close)

Bug 2:trunc还是floor(commit7cc88f00141)。把 f64 秒拆成 timespec 时用trunc,对 1970 年前的负数时间会算出负的 nsec,这是非法 timespec;floor才能保证 nsec 落在[0, 1e9)

let sec = t.trunc(); TimeLike { sec: sec as i64, nsec: ((t - sec) * 1e9) as i64, } // 修复:let sec = t.floor(); 并对 nsec 取 .round()

Bug 3:unwrap_or的急切求值(commit90111846a14)。color-mix()缺省百分比时,unwrap_or会急切求值参数表达式,导致另一侧百分比缺失时直接 panic;应改用惰性的unwrap_or_else

let p1 = first.percentage.unwrap_or(1.0 - second.percentage.unwrap()); // 修复:unwrap_or_else(|| 1.0 - second.percentage.unwrap())

这三个例子极具教学价值:它们恰好对应内存生命周期、数值边界语义、求值时机三类典型陷阱,且都不是“能跑就行”能发现的。对抗式审查的价值在于——当某个流程产出的代码出问题时,去修流程而不是手动修代码,这样新生成的代码就不会再犯同类错误。

五、迁移结果:内存、体积、性能的实测变化

完成迁移后,Bun 在多个维度出现可测量的改善。以下数据来自中文技术媒体的报道,读者应以官方后续发布的基准为准,但可作为量级参考。

内存占用是改善最显著的维度。据媒体报道,原 Zig 版本单次构建泄漏约 3 MB,连续 2000 次构建后内存占用高达 6745 MB;Rust 版本同等条件下仅 609 MB,且原生内存分配可被 LeakSanitizer 完整追踪。这与前文分析的“Drop 自动清理修复错误路径泄漏”完全吻合。

安装包体积缩减约 20%:通过代码折叠、ICU 数据清理、zstd 延迟解压等优化,Linux 版从 88 MB 降至 70 MB,Windows 版从 94 MB 降至 76 MB。

性能呈小幅提升(约 2%–5%,Linux Xeon 铂金平台测试):HTTP 服务吞吐从 169.6k req/s 升至 177.7k req/s(+4.8%),Next.js 构建从 13.62s 压缩至 13.03s(+4.5%),Claude Code 启动速度优化约 10%。需要强调的是,机械移植策略本身不以性能提升为目标,这些收益更多来自 Rust 编译器优化与更干净的资源管理,而非刻意调优。

稳定性方面,新版一次性修复了旧版本 128 个顽固 bug,测试套件在 Linux/macOS/Windows 全平台通过。同时官方承认仍有约 4%(约 1.3 万处)unsafe代码残留,计划在后续版本持续清理为地道 Rust 写法。

六、局限性与争议

把这次迁移神化是不诚实的。它在 Hacker News 引发两极讨论,质疑声同样有力。

第一是“vibe coding”的可维护性担忧。百万行 LLM 生成代码即便测试全绿,长期可维护性仍存疑。官方博客自己也承认,当前 Rust 代码“看起来像从 Zig 转译出来的”,仍有约 4% 的unsafe与非地道写法,需要后续多轮重构。这意味着 v1.4.0 更像是一个“功能等价但风格未定型”的起点,而非终点。

第二是动机争议。有观点认为这是 Anthropic 借 Bun 刷 AI 能力案例的营销行为;也有 Zig 支持者指出,同等人力投入下优化 Zig 也能达到相近效果,换语言未必必要,甚至有传闻称此举与 Zig 社区拒绝 LLM 贡献的路线冲突有关。这些说法目前缺乏直接证据,但提示读者:单一来源的叙事需要保留判断。

第三是数据溯源问题。本文中“100% 测试通过率”“1448 文件”“约 16.5 万美元 API 成本”“59 亿输入 token”等数字多来自中文媒体转述,官方博客确认了 535,496 行 Zig、11 天、约 50 个工作流、+100 万行 PR、预发布 Claude Fable 5 等核心事实,但完整的成本与通过率明细需以官方后续披露为准。

第四是一个值得玩味的细节:截至本文核查时,Bun 在 GitHub 主分支的 README 仍写着 “It's written in Zig and powered by JavaScriptCore”,而仓库中已并存Cargo.tomlCargo.lockrust-toolchain.tomlCLAUDE.md.claude/目录。这反映出迁移处于“主分支已并入 Rust、稳定版仍以 v1.3.x(Zig)为准、v1.4.0 走 canary”的过渡态,文档与代码尚未完全同步,普通用户通过bun upgrade --canary才能尝鲜。

七、结论与启示

抛开争议,Bun 这次迁移给前端与基础设施开发者留下三条可复用的工程经验。

其一,测试套件的语言无关性是大规模重写的真正杠杆。正因为它用 TypeScript 写测试,迁移后百万级断言原样复用,才让“机械移植 + 全量验证”成为可能。任何可能面临技术栈演进的系统级项目,都应刻意把测试与实现语言解耦。

其二,编译期约束优于风格指南。Bun 那一长串 use-after-free/double-free/泄漏,根因是 Zig 把内存生命周期交给了人的注意力;Rust 用Drop把它交给类型系统后,这类错误直接变成编译错误。选语言时,应优先让语言的约束对准项目最痛的那类 bug。

其三,对抗式审查循环是 LLM 大规模生成代码的质量底线。1 个实现者 + 2 个以上独立上下文的对抗审查者,加上“出问题修流程而非修代码”的原则,把“敢于合并百万行 AI 代码”从赌博变成可重复的工程动作。这套模式不仅适用于语言迁移,也适用于任何 LLM 参与的大型重构。

Bun 的 Rust 版仍在 canary 阶段,unsafe清理与地道化是后续漫长工作。但它已经证明:在 2026 年,一次原本需要一整年的语言级重写,可以借助“语言无关测试 + 机械移植 + 对抗式审查”在 11 天内完成且可验证。这对前端工具链“全面换芯”的浪潮——TypeScript 7.0 选 Go、Astro 7 与 Vite 8 选 Rust——是一个有力的注脚。

相关开源仓库与官方资料:

  • Bun 仓库:GitHub - oven-sh/bun: Incredibly fast JavaScript runtime, bundler, test runner, and package manager – all in one · GitHub
  • 官方博客“Rewriting Bun in Rust”:Rewriting Bun in Rust | Bun Blog
  • 作者整理的前端工具链实践合集:GitHub - wangzifan396-wzf/TW: AI 可视化实验室集 · 176 个交互式项目 · 1408+ 模块 · 零外部依赖 · 纯 HTML/CSS/JS + SVG · 覆盖 AI/ML、CS 系统、计算理论、交叉学科全谱系 · GitHub
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/1 5:44:05

Flutter Clip组件在OpenHarmony平台的实战应用

1. 项目概述&#xff1a;Flutter与OpenHarmony的Clip组件实战在移动应用开发领域&#xff0c;Flutter因其出色的跨平台能力和丰富的UI组件库而广受欢迎。而OpenHarmony作为新兴的操作系统平台&#xff0c;其与Flutter的结合为开发者提供了更多可能性。Clip组件家族正是Flutter中…

作者头像 李华
网站建设 2026/8/1 5:43:46

电动车闯红灯检测数据集 建立基于深度学习Yolov5电动车闯红灯检测识别 pytorch 深度学习目标检测算法yolov5训练

pytorch 深度学习目标检测算法yolov5训练电动车闯红灯检测数据集 建立基于深度学习Yolov5电动车闯红灯检测识别 文章目录 pytorch 深度学习目标检测算法yolov5训练电动车闯红灯检测数据集 建立基于深度学习Yolov5电动车闯红灯检测识别1. 数据准备数据集结构数据标注数据划分 2.…

作者头像 李华
网站建设 2026/8/1 5:36:44

G-Helper完整实战指南:5个技巧彻底释放华硕笔记本性能

G-Helper完整实战指南&#xff1a;5个技巧彻底释放华硕笔记本性能 【免费下载链接】g-helper Lightweight Armoury Crate alternative for Asus laptops with nearly the same functionality. Works with ROG Zephyrus, Flow, TUF, Strix, Scar, ProArt, Vivobook, Zenbook, Ex…

作者头像 李华
网站建设 2026/8/1 5:36:16

C# WinForm控件透明背景实现原理与四大实战方案详解

1. 从“不透明”到“透明”&#xff1a;一个看似简单却暗藏玄机的需求在C# WinForm开发中&#xff0c;给控件设置一个透明背景色&#xff0c;听起来是个再基础不过的需求。无论是想做一个异形按钮、一个带圆角的Panel&#xff0c;还是想实现控件之间的视觉叠加效果&#xff0c;…

作者头像 李华
网站建设 2026/8/1 5:28:03

Matlab中3次B样条曲线优化实践与性能提升

1. 3次B样条曲线在Matlab中的核心价值在工程计算和科学可视化领域&#xff0c;3次B样条曲线因其出色的局部控制性和连续性&#xff0c;成为曲线拟合的首选工具。相比传统多项式拟合&#xff0c;它能有效避免Runge现象&#xff08;高次多项式在区间端点处的剧烈振荡&#xff09;…

作者头像 李华