用 Rust 重写 Bun

链接
分类
文章开发科技2026-07-09 03:34:00
简介

靠人力、规范和测试,无法根治内存问题。文章还披露了一些重写细节,包括使用 Fable 级别 LLM 的成本,语言无关的测试集,对抗式 review 等。

相似条目

笔记

Beta

本文主角:由 Jarred 主导的 JS 运行时,最初从 esbuild 的 Go 转译器逐行移植到 Zig,并扩展为转译/打包、npm 兼容包管理、类 Jest 测试、Node API 与 HTTP/WebSocket 等超大范围产品;现 CLI 月下载超 2200 万,获 Claude Code、Vercel 等采用。因 Zig 下 GC 与手工内存混用导致大量 use-after-free、泄漏与崩溃,团队在 2025 年 12 月被 Anthropic 收购后,用 Claude 将约 53.5 万行 Zig 机械式整体改写为 Rust,11 天(5/3–5/14)内 6778 次提交、测试零跳过,v1.3.14 为末代 Zig 版、v1.4.0 为首个 Rust 版。改写后可修 128 个旧版可复现缺陷,内存泄漏显著收敛(如重复 Bun.build 不再每轮泄漏约 3MB)、二进制体积约缩 20%、HTTP/构建约快 2%–5%,并引入借用检查、Miri、覆盖率引导 fuzz 等系统化稳定性手段;合并后已知 19 处语义回归均已修复。

本文中 Bun 从 Zig 整体迁移的目标语言。相对 Zig 的显式 defer,Rust 的 Drop/RAII 与借用检查把 use-after-free、double-free、错误路径漏释放变成编译期错误,更适合 Bun 中 GC 值与手工内存交织的场景;并带来跨 C/C++ 的 LTO 内联、栈槽复用(llvm.lifetime)等副作用,使性能略升、栈占用下降、二进制变小。改写策略是先忠实机械移植再逐步减 unsafe:合并时约 4% 代码在 unsafe 中(约 1.3 万处关键字、约 2.7 万行/78 万行),多数单行调 C++/C 库。拆成约百个 crate 后曾用工作流消解循环依赖与约 1.6 万编译错误;回归多来自 debug_assert! 擦除副作用、切片奇偶长度、Release 保留边界检查、无 comptime 格式串等语义差异。

Bun 的原实现语言:作者 2021 年 4 月因语言参考与底层性能取向入门,一年内在无 LLM 条件下用 Zig 搭出超大范围运行时,文中明确称「没有 Zig 就做不到」。Zig 强调无隐藏控制流、用 defer/errdefer 显式清理,对许多项目合适,但在 Bun 中与 JavaScriptCore 的异常/GC 规则混用时,生命周期难表达,成为泄漏与崩溃主因;团队虽已打 ASAN、Windows ReleaseSafe、Fuzzilli 与泄漏测试,仍疲于个案修复。作者不归咎于语言本身,并曾考虑自研智能指针/风格指南,但 ergonomics 与保证不如 Rust;v1.3.14 为最后 Zig 版。移植中也暴露 ReleaseFast 去掉边界检查、assert 始终求值、comptime 格式串等与 Rust 的语义落差。

Anthropic 预发布的 Mythos 级模型,作者声明用其完成了本次 Bun Rust 改写的主体工作。配合 Claude Code 动态工作流,在约 11 天内驱动峰值约 64 个 Claude 并行:生成 PORTING.md/LIFETIMES.tsv、机械移植 1448 个 .zig 文件、消编译错误与循环依赖、跑通子命令与全量测试(零跳过),峰值约每分钟 1300 行代码,并经双对抗评审后再提交。文中强调若无此模型与工作流,团队不会停功能去手写一年级改写,现实替代只会是继续个案修漏洞。

文中双重角色:一是改写执行平台——作者用约 50 个动态工作流连续跑 11 天,采用「1 实现 + ≥2 对抗评审 + 1 修复」、分 worktree 分片、禁止危险 git/慢命令等约束,并靠语言无关 TS 测试套件建立合并信心;合并后还用 Claude Code Security 完成 11 轮安全审查,fuzz 发现的问题也会交给 Claude 提复现与修复 PR。二是产品侧用户——Claude Code 等工具本身押注 Bun 作运行时;v2.1.181(6 月 17 日)起切到 Rust 版 Bun,Linux 启动 p50 约从 517ms 到 464ms(约快 10%),其余几乎无感。动态工作流被作者视为否则需自写 harness 才能支撑 64 并发长跑的关键能力。

本文中 JavaScript 既是 Bun 的核心能力边界(转译/打包/压缩、模块解析与大量运行时 API),也是稳定性难题的根源:它是垃圾回收语言,与 Zig 手写内存管理混用时,异常处理与 GC 可见性极易出错。Rust 重写后还对 JS 解析器做了全天候覆盖引导模糊测试,并用 Drop 等机制降低泄漏与崩溃。

TypeScript 贯穿 Bun 的产品范围:转译器、兼容的模块解析,以及用 TypeScript 编写、与实现语言无关的测试套件,使整仓从 Zig 换到 Rust 后仍能复用同一套断言。文中还以符号合并等逻辑说明 TS 语义(如 import 可与模块内符号静默冲突)在机械移植中被原样保留,并对 TS 解析器持续 fuzz。

Bun 定位包含 Node 兼容的模块解析与大量 Node API(如 fs、net、tls 等);文中列举的多起 use-after-free、泄漏就发生在 node:zlib、node:http2 等路径上。作者认为若人工重写一年会冻结 Node 兼容性与安全修复,而 Rust 版在 v1.4 后修复大量相关缺陷,并在 node:http 吞吐等基准上相对 v1.3.14 有约 4%–5% 提升。

文章将 Fuzzilli 列为 Bun 在 Zig 时代已采用的稳定性手段之一:对运行时 API 做 7×24 小时模糊测试。它被说明为 V8 与 JavaScriptCore 也在使用的 JavaScript 引擎模糊测试器。作者借此说明团队已投入大量检测,但仍难系统杜绝 use-after-free、泄漏等与手动内存管理相关的问题,从而引出改用 Rust 的动机。

Bun 团队曾为 Zig 编译器打补丁以支持 AddressSanitizer,并在每次提交上用 ASAN 跑测试套件,作为早期发现问题的手段之一。文章同时指出:即便有 ASAN,若改用 C++ 仍会依赖风格指南与代码评审,内存破坏与泄漏仍可能发生。相对之下,Rust 的安全子集把大量同类问题变成编译期错误,反馈环更早、更硬。

Rust 重写后,团队强化了 LeakSanitizer 与原生分配的对接,用以跟踪全部可检测的原生内存分配,并声称已修掉所有可被该工具观测到的泄漏。文中以进程内反复调用 Bun.build() 为例:v1.3.14 每次构建约泄漏数兆字节、2000 次后内存飙到约 6.7 GB,而 v1.4.0 内存在约 609 MB 附近趋于平稳。作者还提到,Zig 侧曾因缺少类似 Drop 的自动析构而不敢合入同类修复。

文章在讨论“为何不整体改用 C++”时,把 BoringSSL(Google 对 OpenSSL 的分支)列为 Bun 已嵌入的 C/C++ 依赖之一。前文稳定性问题清单中也出现与 TLS/SSL 相关的泄漏(如错误路径未释放口令串、setSession 未释放 SSL_SESSION)。重写到 Rust 后仍继续依赖这类 C/C++ 库,因此代码中会保留一定比例的 unsafe 边界调用。

SQLite 在文中作为 Bun 所嵌入的 C/C++ 库之一被点名,与 JavaScriptCore、uWebSockets、BoringSSL 等并列。作者用“约 20% 代码为 C++ 且依赖这些库”说明:即便改用 C++ 也能获得构造/析构,但仍难靠风格与评审根治内存问题,因而最终选择 Rust,同时承认会长期保留对包括 SQLite 在内的 C/C++ 组件的调用。

本文中,借用检查器是 Bun 从 Zig 迁到 Rust 后获得的核心语言能力之一。作者指出大量 use-after-free、double-free 与错误路径上“忘记释放”类问题,在安全 Rust 里会直接变成编译错误,并由带 Drop 的 RAII 自动清理,反馈循环优于靠风格指南与人工 review 的 Zig/C++ 路线。机械移植时目标是在保持架构与行为不变的前提下先拿到借用检查等能力,再逐步减少 unsafe、走向更惯用的 Rust。文末将其与 Miri 等一并列为 v1.4 系统性提升稳定性的关键工具。

本文把 Miri 列为 Bun 完成 Rust 重写后用于系统性提升稳定性的工具之一,并说明其已在 CI 中对不断扩大的代码范围运行。它与 Rust 借用检查器、LeakSanitizer 以及 7×24 覆盖引导的解析器模糊测试并列,支撑团队在 v1.4 之后继续压低内存与未定义行为类问题,而不是继续在 Zig 时代靠逐个修 bug。

该分类暂无内容
该分类暂无内容
该分类暂无内容

本文主角:由 Jarred 主导的 JS 运行时,最初从 esbuild 的 Go 转译器逐行移植到 Zig,并扩展为转译/打包、npm 兼容包管理、类 Jest 测试、Node API 与 HTTP/WebSocket 等超大范围产品;现 CLI 月下载超 2200 万,获 Claude Code、Vercel 等采用。因 Zig 下 GC 与手工内存混用导致大量 use-after-free、泄漏与崩溃,团队在 2025 年 12 月被 Anthropic 收购后,用 Claude 将约 53.5 万行 Zig 机械式整体改写为 Rust,11 天(5/3–5/14)内 6778 次提交、测试零跳过,v1.3.14 为末代 Zig 版、v1.4.0 为首个 Rust 版。改写后可修 128 个旧版可复现缺陷,内存泄漏显著收敛(如重复 Bun.build 不再每轮泄漏约 3MB)、二进制体积约缩 20%、HTTP/构建约快 2%–5%,并引入借用检查、Miri、覆盖率引导 fuzz 等系统化稳定性手段;合并后已知 19 处语义回归均已修复。

Bun 的原实现语言:作者 2021 年 4 月因语言参考与底层性能取向入门,一年内在无 LLM 条件下用 Zig 搭出超大范围运行时,文中明确称「没有 Zig 就做不到」。Zig 强调无隐藏控制流、用 defer/errdefer 显式清理,对许多项目合适,但在 Bun 中与 JavaScriptCore 的异常/GC 规则混用时,生命周期难表达,成为泄漏与崩溃主因;团队虽已打 ASAN、Windows ReleaseSafe、Fuzzilli 与泄漏测试,仍疲于个案修复。作者不归咎于语言本身,并曾考虑自研智能指针/风格指南,但 ergonomics 与保证不如 Rust;v1.3.14 为最后 Zig 版。移植中也暴露 ReleaseFast 去掉边界检查、assert 始终求值、comptime 格式串等与 Rust 的语义落差。

文首披露:2025 年 12 月收购 Bun,作者及 Bun 团队现供职于此。本次 Zig→Rust 改写高度依赖其预发布大模型 Claude Fable 5 与 Claude Code 动态工作流(含对抗式评审、安全审查产品 Claude Code Security),使「一年三人全职」级别的机械移植在约 11 天、约 16.5 万美元 API 成本量级内完成,并在合并后持续支撑 fuzz 自动提 PR 与多轮安全评审。Anthropic 既是组织归属方,也是改写得以可行的模型与工具供给方。

Bun 最初是把 esbuild 的 JavaScript/TypeScript 转译器从 Go 逐行移植到 Zig 而来的;作者也以这段「整包一次性移植」经验,论证本次用 LLM 把整个 Bun 从 Zig 机械搬到 Rust 时,整仓重写比增量改写更合适。

本文中 JavaScript 既是 Bun 的核心能力边界(转译/打包/压缩、模块解析与大量运行时 API),也是稳定性难题的根源:它是垃圾回收语言,与 Zig 手写内存管理混用时,异常处理与 GC 可见性极易出错。Rust 重写后还对 JS 解析器做了全天候覆盖引导模糊测试,并用 Drop 等机制降低泄漏与崩溃。

Bun 定位包含 Node 兼容的模块解析与大量 Node API(如 fs、net、tls 等);文中列举的多起 use-after-free、泄漏就发生在 node:zlib、node:http2 等路径上。作者认为若人工重写一年会冻结 Node 兼容性与安全修复,而 Rust 版在 v1.4 后修复大量相关缺陷,并在 node:http 吞吐等基准上相对 v1.3.14 有约 4%–5% 提升。

JavaScriptCore 是 Bun 嵌入的 Safari JS 引擎,约占其 C/C++ 依赖的核心部分,并带来严格的异常与 GC 规则;与 Zig 手动内存混用是泄漏与崩溃的主要来源。Rust 重写后仍继续依赖 JSC,因此代码中会有不少面向 C++/JSC 的单行 unsafe;团队也用 Fuzzilli 等对运行时 API 做持续 fuzz。

文中把 V8 与 JavaScriptCore 并列为现代 JS 引擎,强调其对异常处理与垃圾回收有严格约定,从而解释为何在非 GC 语言里托管 JS 值生命周期极易出错。Bun 还采用 V8 与 JSC 同款引擎模糊测试工具 Fuzzilli,对运行时 API 进行 24/7 fuzz,作为 Zig 阶段已有、Rust 阶段继续加强的稳定性手段。

Prisma 被作为生产落地案例:其 Prisma Compute 公开测试版跑在 Bun 的 Rust 重写之上。负责人 Alexey Orlenko 称旧版本存在内存泄漏,且连接池在虚拟机暂停恢复后无法自愈;用 Rust 版 Bun 复现相同故障场景后表现正常。文章以此说明重写不仅通过测试,也已服务真实产品工作负载。