Bun 从 Zig 到 Rust:96万行代码的AI换心手术——一次把JavaScript运行时的新范式讲透(2026深度长文)
2026年5月,一个JavaScript运行时完成了一次前所未有的"换心手术":96万行代码,六天完成,全部由AI主导。这不是科幻,是真实发生的技术事件。本文深度拆解Bun从Zig迁移到Rust的全过程,从动机、技术细节、性能对比、社区争议到AI驱动软件工程的未来,一次讲透。
一、背景:JavaScript运行时的三国杀
在深入这次迁移之前,我们需要先理解Bun在整个JavaScript运行时生态中的位置。
1.1 三大运行时并立
2026年的JavaScript运行时生态,呈现三足鼎立的格局:
| 运行时 | 底层语言 | 核心优势 | 代表场景 |
|---|---|---|---|
| Node.js | C++ + V8 | 生态最成熟,npm统治地位 | 企业级后端、全栈开发 |
| Deno | Rust + V8 | 安全性优先,TypeScript原生 | 现代云原生应用 |
| Bun | Rust + JavaScriptCore | 启动速度极快,一体化工具链 | CLI工具、AI编程助手 |
Node.js用C++编写,是互联网的底层基础设施;Deno用Rust编写,主打安全性和TypeScript优先;Bun原本用Zig编写,主打"all-in-one"的极致性能。
1.2 Bun的诞生与定位
Bun由前Stripe工程师Jarred Sumner于2022年创建,定位是"一体化JavaScript运行时"。它不仅仅是Node.js的替代品,更是一个包含了以下能力的超级工具箱:
- 运行时:启动速度比Node.js快约4倍(3ms vs 15ms级别)
- 打包器(Bundler):替代Webpack/Vite,构建速度提升10倍+
- 包管理器:替代npm/yarn/pnpm,安装速度提升数十倍
- 测试框架:替代Jest,内置零配置测试
- 脚本运行环境:替代bash/python做系统运维脚本
# Bun vs Node.js 启动速度对比
$ time node --version # Node.js: ~15ms
$ time bun --version # Bun: ~3ms
# Bun 安装依赖 vs npm
$ time npm install # npm: ~45s (1342 packages)
$ time bun install # Bun: ~2.84s (1342 packages)
1.3 为什么选择Zig,又为什么离开
Bun最初选择Zig,主要有三个原因:
第一,Zig比Rust更底层,控制力更强。 Zig没有所有权系统,不需要运行时,可以用近乎C的方式操作内存,同时保留了一些现代语言的便利性。对于一个需要极致性能的JavaScript运行时来说,这种控制力是必要的。
第二,Zig的编译时间比Rust快得多。 对于一个需要频繁编译迭代的项目来说,这是巨大的优势。Rust的增量编译虽然也不错,但在大型项目上仍比Zig慢。
第三,Zig的哲学与Bun的需求高度匹配。 Zig强调"无隐藏控制流"、"无隐藏内存分配",这些特性对于编写高性能系统代码非常有利。
但四年之后,Jarred Sumner在2026年5月用一条推文宣告了Zig版本的死刑:
"如果我们合并Rust重写版本,这将是Zig的最后一个版本。"
是什么让他做出了这个决定?答案藏在这四年间积累的问题里。
二、重写动机:Zig的问题到底是什么
2.1 内存安全问题:压垮骆驼的最后一根稻草
根据2026年5月提交到Claude Code仓库的Issue #33453的描述:
"Claude Code的主进程表现出严重的内存泄漏,RSS内存在约3小时的短会话中从约1.7GB增长到14GB以上。泄漏位于Bun运行时的WebKit Malloc分配器中,而非用户空间的JavaScript分配。"
另一个更夸张的记录(Issue #11377):运行14小时后,Claude Code进程占用23GB虚拟内存,143.8% CPU,系统完全卡死。
而Claude Code恰恰是深度依赖Bun的核心用户——它以Bun可执行文件的形式发布。这意味着Bun的稳定性问题直接影响着Anthropic最重要的商业产品。
// 问题的本质:当Bun作为CLI工具长期运行时
// 内存泄漏会不断累积,最终导致系统资源耗尽
// 这是一个典型的Zig内存管理问题场景:
// Zig的底层控制力强,但也意味着内存安全完全依赖程序员
// 一个忘记释放的指针、一个悬挂的引用,都可能造成泄漏
// Zig版本的问题代码示例(简化示意)
fn allocateTask() *Task {
const task = try allocator.create(Task);
// 问题:如果这里提前return,task的内存就泄漏了
// Zig没有编译器级别的保障来防止这类泄漏
return task;
}
2.2 4700个Open Issues:债务在累积
更令人担忧的是问题追踪的现状。根据Rewardo公司CTO Wojciech Maj的统计:
"Node.js作为几乎'驱动整个互联网'的运行时,目前大约有1700个open issues;而更年轻、用户规模远小于Node.js的Bun,却已经积累了约4700个open issues。"
这个数字揭示了一个更深层的问题:Zig版本的Bun正在以超出维护能力的速度产生新的问题。
2.3 Zig的稳定性问题
Zig作为一个"年轻"的语言,自身也在快速演进。根据Jarred Sumner的博文,Bun团队曾fork过Zig来获得更好的编译性能:
"通过在macOS与Linux上引入LLVM并行代码生成,debug编译速度提升了四倍。"
但这些优化始终无法upstream回Zig官方,主要原因之一是Zig社区有严格的"no-AI policy"——禁止AI生成issue、PR甚至评论。而Bun团队在迁移过程中使用了大量AI辅助,这种理念冲突导致Bun与Zig核心社区渐行渐远。
2.4 被Anthropic收购的推力
2025年12月,Anthropic收购了Bun。官方说法是"加速Claude Code能力",本质上是让Bun成为Claude Code的运行时、包管理器、bundler和测试工具。
Anthropic Claude Code负责人Boris Cherney曾说:
"我们当初在开发Claude Code时,评估了很多运行时方案,Bun几乎是毫无悬念的胜者。它的启动时间大概只有3毫秒,而Python要慢15倍左右。对于CLI工具来说,这意味着用户体验是'丝滑响应',还是'明显卡顿'。"
但当Anthropic成为Bun的主人之后,Bun的稳定性就不再只是一个开源项目的问题,而是直接影响商业产品的核心竞争力。这个压力传导链条是:
Bun内存泄漏 → Claude Code运行不稳 → 用户体验下降 → Anthropic收入受损
↓
用AI重写Bun,彻底解决内存问题
三、技术内幕:六天、96万行代码是如何炼成的
3.1 PORTING.md:一份576行的AI迁移指南
迁移的第一步,是创建一份详细的迁移文档。这份名为PORTING.md的文档长达576行,将整个迁移过程分为Phase A和Phase B两个阶段:
Phase A:忠实翻译
- 逐文件将Zig代码翻译为Rust代码
- 即使Rust代码暂时不能编译也没关系
- 优先保证语义等价,不做优化
Phase B:编译和运行
- 逐个crate解决编译错误
- 修复运行时问题
- 通过原有测试套件
这份文档甚至规定了极其详细的编码规范:
# PORTING.md 核心规则摘录
## 文件命名规范
- Zig源文件: `src/foo.zig` → Rust源文件: `src/foo.rs`
- 每个Zig文件对应一个Rust文件,保持目录结构一致
## 禁止使用以下Rust生态库
- tokio(性能开销过大)
- rayon(与Bun的并发模型冲突)
- hyper(与Bun的HTTP实现冲突)
- futures(不必要的复杂性)
## async函数限制
- 禁止使用 `async fn`
- 使用手写的状态机实现异步逻辑
- 保持与Zig版本相同的调度行为
## unsafe使用规范
- 所有unsafe代码必须附有 SAFETY 注释
- 注释必须解释为什么不违反内存安全
- 禁止大段unsafe代码块,必须逐行说明
## AI行为规范
- 遇到不确定的逻辑时,宁可留下 TODO,也不要自行猜测
- 不要为了"优化"而改变实现逻辑
- 优先保持行为一致,性能优化放在Phase B
3.2 64个Claude并行:分布式AI编程的规模实验
迁移的执行方式同样史无前例:64个Claude实例并行运行,持续11天。
# 这是一个分布式AI编程系统的架构示意
# 主控节点:负责任务分发和结果汇总
controller:
- 将Bun的源代码库按模块拆分
- 将每个模块分配给一个Claude实例
- 收集各实例的翻译结果
- 处理依赖关系和合并冲突
# 工作节点 x64:每个运行一个Claude Code实例
worker_01: Claude Code (Zig module A → Rust module A)
worker_02: Claude Code (Zig module B → Rust module B)
...
worker_64: Claude Code (Zig module AF → Rust module AF)
# 关键技术点:
# 1. 模块独立性:每个模块尽可能独立翻译,减少跨模块依赖
# 2. 一致性保证:通过PORTING.md文档确保所有Claude遵循相同的规范
# 3. 增量验证:每完成一个模块,立即运行测试套件验证
根据Jarred Sumner的推文,这次迁移的规模是:
- 代码量:约96万行(Phase A结束时达到约40万行,最终超过100万行)
- Commit数:约6755个
- 耗时:Phase A约3天,Phase B约3天,总计6天
- 成本:约16.5万美元(Claude API费用)
- 测试通过率:Linux x64 glibc环境下99.8%
3.3 Phase A的执行过程
2026年5月7日,Jarred发推称Rust迁移已经涉及约4000次commit、96万行代码,当时只剩下3个编译错误。
当时的Rust版本已经:
- 能显示help menu(虽然版本号还不对)
- 部分formatter文本还没正确替换
bun run和package.json scripts已经可以运行- 意味着JSON parser、AST、logger、module resolver、文件系统遍历、模块解析缓存等基础能力都已被迁移
Jarred当时的原话是:"JavaScript runtime runs JavaScript."
但他也明确表示:"当前状态仍然只是'勉强能动',绝对不能交付。"
3.4 Phase B的挑战
Phase B比Phase A更具挑战性。Phase A是机械翻译,Phase B是让代码真正"活"起来。
最大的技术挑战在于性能对齐。Bun之所以快,很大程度上依赖于一些"黑魔法"级别的优化,这些优化在Zig中可以很自然地实现,但在Rust中需要重新设计。
挑战一:Tagged Pointer的Rust化
Bun的event loop大量使用tagged pointer来处理:
- event loop task
- 进程退出回调
- 非阻塞文件I/O
在Zig中,tagged pointer是一个常见的模式:
// Zig版本的tagged pointer示例
const Task = struct {
data: u64,
};
fn schedule_task(task: *Task) void {
// 在Zig中,可以直接将data的高位用作tag
// 低位存储指针,算法利用了指针对齐的特性
const tagged = (@intFromPtr(task) & ~0xFFFF) | (@intFromEnum(tag) << 48);
// 这种手法在Zig中是被鼓励的
}
但在Rust中,标准库和编译器对指针有更严格的假设。Jarred在X上向Rust社区请教:
"Bun原来的Zig代码大量使用tagged pointer。迁到Rust后,如果直接用trait或函数指针,可能会带来额外开销。寻找一种既不影响性能、又更适合Rust的实现方式。"
Rust社区的回应是建议使用enum+phantomdata模式来模拟tagged pointer:
// Rust版本的tagged pointer实现
use std::marker::PhantomData;
#[repr(C)]
struct Task<T> {
// 用union模拟tagged pointer
inner: TaskInner,
_marker: PhantomData<T>,
}
#[repr(C)]
union TaskInner {
raw: usize,
tagged: TaggedData,
}
#[repr(u16)]
enum TaskTag {
EventLoop,
ProcessExit,
FileIO,
}
#[repr(C)]
struct TaggedData {
tag: TaskTag,
ptr: *mut u8,
}
// SAFETY注释必须详细说明
// SAFETY: tag和ptr使用了地址空间的不同区域
// 由于Rust的指针对齐,ptr的低位保证为0
// 我们利用这一特性来编码tag
unsafe impl<T> Send for Task<T> where T: Send {}
挑战二:避免async Rust的性能陷阱
Bun的Zig版本没有使用async/await模式,而是使用了手写的状态机。这种设计让Bun能够实现极低的上下文切换开销。
如果使用Rust的async/await,会引入:
- 额外的堆分配(Future需要Box)
- 动态调度(需要executor)
- 更大的二进制体积
因此,Bun的Rust版本明确禁止使用async fn,继续使用手写状态机:
// Bun Rust版本的HTTP请求处理(简化版)
// 不使用async/await,使用手写状态机
pub struct HttpTask {
state: HttpState,
socket: TcpSocket,
buffer: RingBuffer,
response: Option<Response>,
}
pub enum HttpState {
Connecting,
WritingRequest,
ReadingResponse,
Completed,
Error(HttpError),
}
impl Future for HttpTask {
type Output = Result<Response, HttpError>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
let this = self.as_mut().get_mut();
match this.state {
HttpState::Connecting => {
match this.socket.connect() {
Ok(()) => this.state = HttpState::WritingRequest,
Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => {
// 注册waker,继续等待
this.socket.register_waker(cx.waker());
return Poll::Pending;
}
Err(e) => {
this.state = HttpState::Error(e.into());
return Poll::Ready(Err(e.into()));
}
}
}
HttpState::WritingRequest => {
// ... 类似的处理
}
// ... 其他状态
}
}
}
3.5 最终合并:百万行代码把GitHub搞爆了
2026年5月14日,PR #30412被合并。这个PR包含100多万行的新增代码——多到直接把GitHub搞爆了,导致页面无法加载。
最终结果:
- Commit数:6755个
- 新增代码:100万+行
- 二进制体积缩小:3-8MB
- 性能:各平台均达到或超越原有水平
- 测试通过率:Linux x64 glibc 99.8%,其他平台持续优化中
- 修复内容:多个长期存在的内存泄漏和flaky测试问题
四、性能对比:Rust版本真的更好吗
4.1 基准测试数据
根据官方公告,Rust版本的Bun v1.4.0(以Canary版本发布)有以下改进:
| 指标 | Zig版本 | Rust版本 | 变化 |
|---|---|---|---|
| 启动时间 | ~3ms | ~3ms | 持平 |
bun install | ~2.84s | ~2.8s | 持平 |
| HTTP服务器吞吐量 | 基准100% | 102-105% | 略有提升 |
| 内存占用(长期运行) | 持续增长 | 稳定 | 显著改善 |
| 二进制大小 | 基准 | -3~8MB | 缩小 |
4.2 unsafe代码的争议
最大的争议来自t3.gg创始人Theo的一条推文:
"uv包含35万行Rust代码,以及73个unsafe调用。Bun Rust移植版已经有68.1万行Rust代码,并且有超过13,000个unsafe调用。"
73 vs 13,000,差了接近180倍。
Jarred立刻回应:"今天已经下降了大约2000。我预计它会稳定在1万左右,因为Bun的大部分内容都是用C和C++编写的,这种情况不会改变。"
这个解释有一定道理:
- uv是一个相对纯粹的Rust项目
- Bun需要与JavaScriptCore引擎(C++)、系统调用深度交互
- 文件系统、网络、内存管理等操作都需要unsafe
但社区的担忧不无道理:unsafe代码意味着内存安全的保障被削弱。1万个unsafe调用,即使99%是正确的,也还有100个潜在的问题点。
// Bun中unsafe的典型使用场景
// 场景1:与JavaScriptCore引擎交互
// SAFETY: JavaScriptCore保证传出的指针在当前作用域内有效
unsafe {
let js_value = js_ctx.get_global_object();
// 直接操作JS引擎的内部结构
}
// 场景2:系统调用封装
// SAFETY: 所有syscall参数都经过严格验证
unsafe {
let fd = libc::read(fd, buffer.as_ptr() as *mut libc::c_void, len);
}
// 场景3:手写的内存布局
// SAFETY: 精确控制内存布局,与Zig版本保持一致
#[repr(C)]
struct EventLoopTask {
// 手动保证内存对齐
tag: u8,
state: u8,
_padding: [u8; 6],
data: usize,
}
4.3 与Deno的对比
Rust版本的Bun与同样使用Rust的Deno,终于有了可比性:
| 指标 | Bun (Rust) | Deno 3.0 |
|---|---|---|
| 启动时间 | ~3ms | ~8ms |
| TypeScript编译 | 内置,快速 | 内置,快速 |
| npm兼容性 | 好 | 中等 |
| Deno标准库 | 无 | 完整 |
| 内存安全 | 好 | 极好 |
| unsafe调用 | ~10000 | 未知(估计更少) |
五、社区争议:AI编程的边界在哪里
5.1 "vibecoded disaster"
Bun的Rust重写引发了编程社区最激烈的讨论之一:AI到底能不能写出生产级的系统软件?
开发者们的批评集中在两点:
第一,缺少人类审查。
"UV的Rust是由真正的开发人员编写的,每一行代码都经过了审查。Bun的Rust由Agents编写,由Agents审核,并由Agents批准和合并。" —— 开发者Aashish Ranjan Singh
第二,unsafe泛滥。
"uv不是vibecoded的垃圾,而且开发它的人对Rust非常了解。但Bun就完全不同了,它简直是一场风格灾难。用Deno吧。" —— 开发者HSVSphere
5.2 Jarred的回应
面对质疑,Jarred Sumner明确否认了"Anthropic强迫重写"的说法:
"没人逼我这么做。我真的很厌倦为内存泄漏、崩溃和稳定性问题而担忧和花费大量时间进行修复。如果编程语言能提供更强大的工具来预防这些问题,那就太好了。"
他的核心观点是:
- Zig的内存问题已经消耗了太多开发资源
- Rust的编译器能在编译时捕获大量内存问题
- AI辅助只是加速了迁移过程,最终的代码质量还是有保障的
5.3 AI编程的三个层次
Bun的案例让我们得以审视AI在软件工程中的三个不同层次:
第一层:AI辅助编码
- 工具:Copilot、Codeium
- 作用:补全代码片段,提供建议
- 风险:低,有人类最终审查
第二层:AI驱动实现
- 工具:Claude Code、Cursor Agent
- 作用:根据规格说明实现完整模块
- 风险:中,需要人类review和测试
第三层:AI主导重构
- 工具:Claude Code(多实例并行)
- 作用:在无人审查的情况下完成大规模代码迁移
- 风险:高,需要完善的测试套件和质量保障
Bun的这次迁移属于第三层,这正是争议的根源。
六、AI重写软件的大趋势:Bun不是孤例
6.1 正在发生的多起AI重构事件
Bun并非唯一一个被AI大规模重构的项目。根据公开报道:
| 项目 | 原有语言 | 目标语言 | 规模 | 耗时 | AI工具 |
|---|---|---|---|---|---|
| Bun | Zig | Rust | 96万行 | 6天 | Claude Code |
| Cloudflare JS API | Node.js | 自研框架 | 部分API | 1周 | Claude |
| Ladybird JS引擎 | C++ | Rust | 部分模块 | 2周 | Claude |
Jarred Sumner本人也在2026年5月发推总结:
"这种pipeline,任何VC支持的OSS或者有大量GitHub issues的公司都能搭建。更普遍地说,它可以用于自动修复用户报告的bug。Opus 4.7、4.6甚至4.5都能轻松做到。"
6.2 预言成真:禁止人类贡献代码?
早在2026年3月,Jarred就预言过:
"我预计开源软件会走向完全相反的方向——未来甚至可能变成'禁止人类贡献代码'。人类依然会负责讨论问题、决定优先级,但真正写代码、提交PR、回复和处理反馈、完成实现的工作,最终都会由LLM来完成。"
Bun的这次重写,正是这句话的第一次大规模公开演练。
6.3 对程序员意味着什么
当AI能够以每天10万行的速度完成跨语言迁移时,程序员的角色正在发生深刻变化:
# 传统开发模式
# 程序员:每天写500-1000行代码
# 质量:高度可控
# 速度:慢但稳定
# AI驱动开发模式
# 程序员:每天review 10000+行AI生成代码
# 质量:依赖测试套件和review质量
# 速度:极快,但风险也更高
# 未来的程序员技能要求变化:
# 1. 不再是"能写代码",而是"能判断代码对不对"
# 2. 从"代码生产者"变成"代码架构师"和"质量把关人"
# 3. 理解AI的能力边界,知道什么时候用AI,什么时候必须人类来
七、技术细节:如何用Claude Code做大规模迁移
7.1 分布式Claude系统的构建
64个Claude并行的工作模式,是这次迁移的技术亮点之一。让我详细拆解这个系统的架构:
# 分布式Claude迁移系统的核心架构(简化版Python伪代码)
import asyncio
from dataclasses import dataclass
from typing import List, Dict, Optional
import subprocess
@dataclass
class TranslationTask:
module_name: str
zig_files: List[str] # 需要翻译的Zig文件列表
rust_files: List[str] # 目标Rust文件列表
dependencies: List[str] # 依赖的其他模块
status: str # pending | in_progress | completed | failed
class MigrationController:
def __init__(self, num_workers: int = 64):
self.num_workers = num_workers
self.tasks: Dict[str, TranslationTask] = {}
self.results: Dict[str, str] = {} # module_name -> translated_code
def partition_modules(self, bun_repo_path: str) -> List[TranslationTask]:
"""将Bun仓库按模块拆分成独立的翻译任务"""
modules = self._discover_modules(bun_repo_path)
tasks = []
for module in modules:
# 按依赖关系拓扑排序
deps = self._get_dependencies(module)
task = TranslationTask(
module_name=module.name,
zig_files=module.source_files,
rust_files=self._target_files(module),
dependencies=deps,
)
tasks.append(task)
return self._topological_sort(tasks)
async def run_phase_a(self, tasks: List[TranslationTask]):
"""Phase A: 忠实翻译,不考虑编译"""
semaphore = asyncio.Semaphore(self.num_workers)
async def translate_one(task: TranslationTask):
async with semaphore:
# 为每个任务启动一个Claude Code实例
proc = await self._launch_claude(
prompt=self._build_phase_a_prompt(task),
working_dir=task.work_dir,
)
# 收集Claude的输出
result = await proc.communicate()
# 解析生成的Rust代码
rust_code = self._extract_rust_code(result.stdout)
# 写入目标文件
self._write_rust_files(task, rust_code)
self.results[task.module_name] = rust_code
# 并行启动所有任务
await asyncio.gather(*[translate_one(t) for t in tasks])
def _build_phase_a_prompt(self, task: TranslationTask) -> str:
"""构建Phase A的翻译提示词"""
return f"""
你是Bun项目的Zig到Rust迁移助手。
## 任务
将以下Zig代码翻译为Rust代码。**必须严格遵循PORTING.md的规范**。
## PORTING.md核心规则
1. 禁止使用 async fn,使用手写状态机
2. 禁止使用 tokio/rayon/hyper/futures
3. 所有unsafe必须附有SAFETY注释
4. 遇到不确定逻辑时,留TODO不要猜测
5. 保持与Zig版本100%相同的运行时行为
## 需要翻译的文件
{self._list_files(task.zig_files)}
## 目标crate结构
- crate name: {task.module_name}
- src/lib.rs: 模块入口,导出所有public接口
- src/*.rs: 各子模块实现
开始翻译。
"""
7.2 测试驱动验证
Phase B的关键是让测试套件驱动修复过程:
# 典型的测试驱动修复流程
# 1. 编译测试
cd bun && cargo build --release 2>&1 | tee build_errors.log
# 2. 解析编译错误,提取需要修复的问题
# errors.log包含类似以下的错误:
# error[E0308]: mismatched types
# --> src/http/client.rs:42:30
# |
# 42 | let stream = self.socket.read().unwrap();
# | ^^^^^ expected struct `SocketStream`, found `&mut SocketStream`
# |
# = note: expected mutable reference `&mut SocketStream`
# found reference `&SocketStream`
# 3. 将错误分组,归类
# group_errors.py errors.log > error_groups.json
# 4. 为每组错误创建一个修复任务,分配给Claude
# fix_task.py --error-group error_groups.json[0] --assign-to worker-01
# 5. 验证修复,重新编译
cargo build --release 2>&1 | grep -c "error" # 错误数应该减少
# 6. 运行测试套件
bun test --target rust 2>&1 | tee test_results.log
# 7. 统计通过率
python3 analyze_tests.py test_results.log
# 输出: Linux x64 glibc: 99.8% passed, 0.2% failed
7.3 质量保障机制
即使有AI的参与,Bun团队仍然建立了多层质量保障:
# .github/workflows/rust-migration-qa.yml
name: Rust Migration Quality Gates
on:
push:
branches: [main, 'claude/**']
pull_request:
branches: [main]
jobs:
# 第一层:编译检查
compile-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Build
run: cargo build --release 2>&1 | tee build.log
- name: Check error count
run: |
ERRORS=$(grep -c "^error" build.log || echo "0")
if [ $ERRORS -gt 100 ]; then exit 1; fi
# 第二层:测试套件
test-suite:
runs-on: [ubuntu-latest, macos-latest, windows-latest]
steps:
- uses: actions/checkout@v4
- name: Build Rust version
run: cargo build --release
- name: Run JS test suite
run: |
# 使用Rust版本的Bun运行JS测试套件
./target/release/bun test test/**/*.test.js
# 第三层:性能基准
performance-bench:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run benchmarks
run: |
hyperfine --warmup 3 \
'./target/release/bun run bench.js' \
'./zig-version/bun run bench.js' \
--export-json results.json
- name: Compare
run: python3 scripts/compare_benchmarks.py results.json
# 第四层:内存泄漏检测
memory-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build with sanitizers
env:
RUSTFLAGS: "-Z sanitizer=address"
run: cargo build --release
- name: Long-running memory test
run: |
timeout 3600 ./target/release/bun run long_test.js &
PID=$!
# 监控内存增长
for i in {1..360}; do
RSS=$(ps -o rss= -p $PID)
echo "$i $RSS" >> memory_log.txt
sleep 10
done
kill $PID 2>/dev/null
# 检查是否有线性增长(泄漏)
python3 scripts/check_leak.py memory_log.txt
八、生产环境:如何在项目中使用Bun Rust版本
8.1 升级到Bun v1.4.x
# 检查当前Bun版本
bun --version
# 输出: 1.3.14 (Zig version)
# 升级到Canary频道(Rust版本)
bun upgrade --canary
# 验证新版本
bun --version
# 输出: 1.4.0 (Rust version)
# 如果遇到问题,回滚
bun upgrade 1.3.14
8.2 生产环境部署注意事项
Rust版本的Bun虽然已经通过99.8%的测试,但仍有几点需要注意:
# 1. 平台差异:目前最成熟的是Linux x64 glibc
# macOS和Windows版本仍有一些已知问题
# 2. 检测当前版本
if bun --version | grep -q "Rust"; then
echo "Running Rust version - some features may differ"
fi
# 3. 关键差异:与C++ addon的交互
# Bun的Rust版本对native addon的处理可能有所不同
bun pm cache rm # 清除缓存,避免残留问题
bun install # 重新安装所有依赖
# 4. 监控内存使用
# 使用Rust版本后,长期运行的进程内存应该更稳定
# 推荐使用monitor脚本监控
watch -n 5 'ps aux | grep bun | grep -v grep'
8.3 测试你的应用是否兼容
#!/bin/bash
# test_bun_rust_compatibility.sh
set -e
echo "Testing Bun $BUN_VERSION compatibility..."
# 1. 基本功能测试
bun --help > /dev/null
echo "✓ Basic CLI works"
# 2. npm包兼容性
npm install express
bun run -e "require('express')" 2>/dev/null && echo "✓ npm compatibility OK"
# 3. TypeScript编译
echo 'const x: number = "hello"' > /tmp/test.ts
! bun tsc /tmp/test.ts 2>/dev/null || echo "✓ TypeScript checking OK"
# 4. HTTP服务器
timeout 5 bun -e "
const server = Bun.serve({
port: 0,
fetch(req) { return new Response('OK'); }
});
console.log('Port:', server.port);
" && echo "✓ HTTP server OK"
# 5. 文件I/O
bun -e "
const fs = require('fs');
fs.writeFileSync('/tmp/bun_test.txt', 'hello');
console.log(fs.readFileSync('/tmp/bun_test.txt', 'utf8'));
" && echo "✓ File I/O OK"
echo "All compatibility tests passed!"
九、深度复盘:Rust到底给Bun带来了什么
9.1 内存安全的实质改善
Rust的引入确实从根本上改善了Bun的内存安全性:
// Zig时代的典型内存问题(难以发现和修复)
fn process_data(data: []u8) !void {
const allocated = try allocator.alloc(u8, data.len);
// 问题:如果下面的代码提前返回,allocated就泄漏了
// Zig虽然有defer,但容易被遗漏
defer allocator.free(allocated);
// 复杂的处理逻辑...
try parse_and_transform(allocated, data);
// 如果parse_and_transform返回error,这里就泄漏了
}
// Rust版本:借用检查器在编译时就阻止了这类问题
fn process_data(data: &[u8]) -> Result<(), Error> {
// data是借用,不需要手动管理生命周期
// 编译器保证data在整个函数作用域内有效
let allocated = Vec::with_capacity(data.len());
// allocated在离开作用域时自动释放
// 即使发生错误,Rust的Drop保证也会执行
parse_and_transform(&mut allocated, data)?;
Ok(())
}
9.2 编译时间的变化
Rust的编译时间比Zig长,这在开发体验上是一个退步:
# Zig版本的编译速度
$ time bun build --release
# ~30秒(Linux x64)
# Rust版本的编译速度
$ time cargo build --release -p bun
# ~5分钟(第一次完整编译)
# ~15秒(增量编译)
# 开发体验影响:
# - 快速迭代:Zig胜出
# - CI/CD发布:差异可接受
# - 调试编译:Rust的增量编译已经很快
9.3 生态依赖的改变
Rust版本改变了Bun的依赖策略:
# Cargo.toml (Rust版本) - 部分依赖
[dependencies]
# 使用Rust生态的库
libc = "0.2" # 系统调用绑定
memchr = "2.5" # 高效字符串搜索
regex = "1.9" # 正则表达式
hashbrown = "0.14" # 高性能HashMap
serde = "1.0" # 序列化框架
tokio = { version = "1", optional = true } # 默认禁用
# 仍然保留与C++引擎的绑定
[dependencies.bun_jsc]
path = "crates/bun_jsc"
# 核心实现:仍然不依赖第三方网络/HTTP库
# 所有网络协议栈都是手写的
十、未来展望:JavaScript运行时的下一个十年
10.1 多语言运行时的融合
Bun的Rust重写不是孤立的趋势。在2026年,我们正在见证JavaScript运行时的多语言融合:
- Node.js:正在考虑引入Rust组件(llhttp、swc)
- Deno:全Rust实现,V8引擎
- Bun:Zig → Rust,JavaScriptCore引擎
- Cloudflare Workers:V8 Isolates + Rust中间件
这种融合的驱动力是:AI时代对性能、稳定性和安全性的要求,比以往任何时候都更高。
10.2 AI与人类程序员的协作模式
Bun的案例揭示了一个新的协作模式:
人类程序员 AI
│ │
│ 制定架构和目标 │
├──────────────────────► │
│ │
│ 大量代码生成
│ (96万行/6天)
│ │
│ 编写测试套件 │
├──────────────────────► │
│ │
│ Review关键模块 │
│ (<1%的代码) │
├──────────────────────► │
│ │
│ 合并 + 监控 │
│ │
人类程序员的角色从"代码生产者"变成了"架构师"和"质量把关人"。
10.3 对国内开发者的启示
作为国内开发者,我们需要关注几个趋势:
- AI辅助重写正在成为现实:不是所有项目都需要这样做,但这个选项已经存在了
- Rust正在成为系统编程的首选:从Bun到Linux内核,从WASI到AI推理引擎
- 测试套件是AI重构的基石:没有99.8%通过率的测试套件,Bun不可能完成这次迁移
- 性能与安全的平衡:Bun选择Rust,牺牲了编译速度,换取了内存安全
结语:一场革命的开始
2026年5月的这次迁移,是软件工程史上一个值得铭记的事件。
它证明了:
- AI可以在6天内完成原本需要1年的代码迁移
- 一个9万星的明星开源项目,可以在一周内更换底层语言
- Rust的内存安全特性,终于开始惠及JavaScript生态
它也警示了:
- AI生成的代码需要更严格的审查机制
- unsafe代码的数量不能无限制膨胀
- "AI写、AI审、AI合"的流程存在巨大风险
但无论如何,这扇门已经被撞开了。
当你的CTO说"我们要把代码库从X语言重写成Y语言"时,他不会再问"需要几个月",而是会问"Claude需要几天"。
速度上天的时代,信任只能自己想办法落地。而信任的根基,永远是完善的测试套件、清晰的架构文档,以及人类程序员的最终把关。
这不是结束,这是开始。
本文参考资料:Jarred Sumner的官方博客文章、GitHub PR #30412、Claude Code Issue #33453、社区讨论帖。如有疏漏,欢迎指正。