编程 Bun从Zig到Rust:AI主导96万行代码重构,JavaScript运行时的范式转移

2026-07-11 01:15:21 +0800 CST views 419

Bun从Zig到Rust:AI主导96万行代码重构,JavaScript运行时的范式转移

前言:一场荒诞又魔幻的“换心手术”

2026年5月,GitHub上出现了一个匪夷所思的PR——一个包含了100多万行新增代码的Pull Request,直接把GitHub的页面干崩了。

这个PR来自Bun项目。Bun的创始人Jarred Sumner宣布,Bun的核心运行时已经从Zig语言完全重写为Rust。这还不是最离谱的部分:动手重写的,是被Bun的内存泄漏问题困扰已久的Claude AI——而整个过程只用了6天。

是的,你没有看错。Anthropic的AI编程助手Claude Code,因为内置了Bun运行时,内存占用能从1.7GB飙升到14GB。于是Anthropic让Claude去重写Bun;Bun重写完,再回头继续支撑Claude Code。

这个“AI自己修自己”的闭环,让整个编程社区为之震动。

本文将深入拆解这次重构的技术细节、架构设计、AI工作流,以及它对整个软件工程行业的深远影响。


一、背景:为什么Bun要换“心”?

1.1 Bun的诞生与Zig路线

Bun由前Stripe工程师Jarred Sumner于2023年推出,定位是“下一代高性能JavaScript全栈运行时”,目标是一站式替代Node.js、Deno、esbuild等工具。

Jarred选择Zig构建Bun,有其深层次的考量:

Zig的设计哲学:零成本抽象、无隐藏控制流、精确控制内存
Rust的设计哲学:所有权系统、借用检查器、编译时安全保证

Zig对于性能敏感的系统编程来说非常诱人——它比C更安全,比Rust更轻量,没有垃圾回收器,可以直接内联汇编。但Zig的问题在于:它是一门仍在快速演进中的语言,工具链和标准库都不够成熟。

1.2 内存泄漏:Bun的心腹大患

Bun虽然性能出色,但内存管理问题一直是挥之不去的阴影。

主要问题包括:

Zig中常见的内存问题:忘记释放arena allocator

const arena = std.heap.ArenaAllocator.init(allocator);
defer arena.deinit();

// 复杂的async栈管理中,指针悬挂问题难以追踪
const frame = try asyncContext.allocateFrame();
defer asyncContext.freeFrame(frame); // 容易遗漏或顺序错误

// 错误路径上的内存泄漏
fn process() !void {
    const buf = try allocator.alloc(u8, 1024 * 1024);
    // 如果这里抛出错误,buf永远不会释放
    try doSomethingRisky();
    allocator.free(buf);
}

而这些问题在生产环境中暴露得更严重。Claude Code的开发者报告:

使用Claude Code编程时,初始内存占用 ~1.7GB
几小时后 → ~5-6GB
重度使用后 → ~12-14GB(甚至更高)

问题根源指向了Bun内置的JavaScriptCore引擎与Zig运行时之间的内存交互层——这部分代码极其复杂,边界情况下的内存行为难以预测和调试。

1.3 破局:Rust成为最优解

经过长时间的权衡,Jarred Sumner决定用Rust重写Bun核心。主要原因:

维度ZigRust
内存安全手动管理,容易出错所有权+生命周期,编译时保证
工具链成熟度一般(1.0还未发布)极高(稳定版工具链)
社区生态小众极其庞大
性能优秀优秀
FFI兼容性需要暴露接口有成熟的C ABI
async生态社区各自为政Tokio、async-std等成熟方案

Rust的编译器会在编译期捕获几乎所有类型的内存错误,从根本上杜绝了dangling pointer、buffer overflow、use-after-free等常见漏洞。对于一个每天处理数十亿次请求的JavaScript运行时来说,这种保障的价值难以估量。


二、技术架构:重写不是推倒重来

2.1 保持架构不变的核心原则

这次重写最令人印象深刻的,不是“用了Rust”,而是“保持了架构不变”。

Jarred Sumner在公告中明确表示:

"这次重写保持了两个关键不变:相同的架构设计,相同的数据结构。Bun依然使用极少的第三方库,依然不依赖async Rust。"

这意味着:

// Rust版本的数据结构,与Zig版本保持一致
pub struct Transpiler {
    // 内部状态布局与Zig版本完全相同
    allocator: BumpAllocator,
    source_map: SourceMap,
    scope_stack: Vec<Scope>,
    import_map: HashMap<String, ModuleRef>,
}

impl Transpiler {
    pub fn transpile(&mut self, source: &str) -> Result<Module, TranspileError> {
        let mut parser = Parser::new(source);
        while let Some(token) = parser.next_token()? {
            self.process_token(token)?;
        }
        Ok(Module::finish(&mut self.scope_stack))
    }
}

2.2 零第三方依赖哲学的延续

Bun的一个标志性特点是不依赖任何第三方运行时库——它自己实现了HTTP服务器、文件系统API、数据库驱动等。Rust版本继承了这一哲学:

// 不使用任何第三方crate来处理HTTP
// Bun自己实现了一个轻量HTTP服务器
mod http {
    use std::net::{TcpListener, SocketAddr};
    
    pub struct Server {
        socket: TcpListener,
        router: RouteTable,
    }
    
    impl Server {
        pub fn new(addr: &str) -> Result<Self> {
            let socket = TcpListener::bind(addr)?;
            socket.set_nonblocking(true)?;
            Ok(Server { socket, router: RouteTable::new() })
        }
    }
}

2.3 与JavaScriptCore引擎的绑定层

Bun的JavaScript执行依赖于WebKit的JavaScriptCore(JSC)引擎。重写过程中,最复杂的部分是与JSC的FFI绑定层:

use std::ffi::c_void;
use jsc_sys::*;

pub struct JSContext {
    pub ctx: NonNull<JSGlobalContext>,
    pub allocator: JSGarbageCollector,
}

impl JSContext {
    pub fn new() -> Self {
        let ctx = unsafe {
            let global = JSGlobalContextCreateInWorld(
                std::ptr::null(),
                JSContextGroupCreate(),
                kJSClassDefinitionNone,
            );
            NonNull::new_unchecked(global)
        };
        JSGarbageCollector::register_context(ctx);
        JSContext { ctx, allocator: JSGarbageCollector::new() }
    }
    
    pub fn evaluate(&self, source: &str, source_url: Option<&str>) -> Result<JSValue, JSCError> {
        let js_string = JSStringCreateWithUTF8CString(source.as_ptr() as *const _);
        let exception: *mut JSValueRef = std::ptr::null_mut();
        
        let result = unsafe {
            JSEvaluateScript(self.ctx.as_ptr(), js_string, std::ptr::null(),
                source_url.map(|s| JSStringCreateWithUTF8CString(s.as_ptr() as *const _)).unwrap_or(std::ptr::null()),
                0, exception)
        };
        
        if !exception.is_null() && !exception.read().is_undefined() {
            return Err(JSCError::Execution(exception.read()));
        }
        Ok(JSValue::from_ref(result, self.ctx))
    }
}

// Rust的所有权系统在这里发挥了关键作用:
// 任何JSValue离开作用域时,都会被自动标记为可GC
// 彻底杜绝了Zig版本中的悬挂指针问题
impl Drop for JSValue {
    fn drop(&mut self) {
        unsafe {
            JSGarbageCollector::release_value(self.ctx, self.value_ref);
        }
    }
}

三、AI主导重构:6天96万行代码的工作流

3.1 为什么让AI来写?

Anthropic决定用Claude Code重写Bun,背后是一个务实的逻辑:

代码量大:96万行,手工重写需要数月甚至数年
逻辑相对机械:Zig到Rust的转换规则相对明确(但细节极多)
AI已经深入理解Zig和Rust:Claude Code可以边写边学习两种语言的语义
循环依赖:用Bun修Bun本身不现实,因为Bun就是问题本身

3.2 AI工作流:规则先行,人类把控

Anthropic没有让Claude“自由发挥”,而是为整个过程制定了严格的工程规范:

禁止使用async fn,必须使用手动Future实现
unsafe代码块必须写明SAFETY注释,说明为什么这样做是安全的
遇到不确定的逻辑时,宁可留下TODO,也不要让AI自行猜测
每个Zig函数的Rust等价物,必须通过相同的单元测试
禁止引入新的第三方依赖(cargo.toml.lock不得增加新的crate)

这些规则确保了AI的重写不会引入新的技术债务,同时保留了审查空间。

3.3 六天的工作节奏

根据公开信息,这次重写的节奏是:

天数内容
第1-2天工具链搭建、Zig→Rust转换脚本开发、第一批核心模块迁移
第3-4天主体逻辑迁移、并行工作流(多个Claude实例同时处理不同模块)
第5天集成测试、CI/CD适配、性能基准测试
第6天修复失败用例、提交PR

这6天里,AI完成了4000多次提交,最终通过了Bun原有测试套件99.8%的验证。

3.4 代码转化的典型案例

让我们看一个Zig到Rust的具体转换例子:

Zig版本(原来的代码):

const std = @import("std");
const ArenaAllocator = std.heap.ArenaAllocator;

pub fn FileSystem.readFile(path: []const u8, allocator: *Allocator) ![]u8 {
    const file = try std.fs.cwd().openFile(path, .{});
    defer file.close();
    
    const stat = try file.stat();
    const contents = try file.readAllAlloc(allocator, stat.size);
    return contents;
}

pub fn processJsonFile(path: []const u8, allocator: *Allocator) !void {
    var arena = ArenaAllocator.init(allocator);
    defer arena.deinit();
    
    const contents = try readFile(path, &arena.allocator);
    defer allocator.free(contents);
    
    const parsed = try std.json.parseFromSlice(JsonData, contents, &arena.allocator);
    defer parsed.deinit();
    
    try processData(parsed.value);
}

Rust版本(Claude重写的):

use std::fs;
use std::path::Path;
use std::alloc::{alloc, dealloc, Layout};

pub fn read_file(path: &str) -> Result<Vec<u8>, FileSystemError> {
    let contents = fs::read(path)?;
    Ok(contents)
}

pub fn process_json_file(path: &str) -> Result<(), FileSystemError> {
    let contents = read_file(path)?;
    
    let parsed: JsonData = serde_json::from_slice(&contents)
        .map_err(|e| FileSystemError::JsonParse(e.to_string()))?;
    
    process_data(&parsed)?;
    Ok(())
}

// SAFETY: 此函数直接操作文件描述符
// - fd是通过std::fs::File::file_handle()获取的有效句柄
// - buf的生命周期通过&mut self保证不与调用方重叠
// - 我们保证在函数返回前写入了完整的字节数
unsafe fn write_to_fd(fd: &fs::File, buf: &[u8]) -> std::io::Result<usize> {
    use std::os::fd::AsRawFd;
    let bytes = libc::write(fd.as_raw_fd(), buf.as_ptr() as *const c_void, buf.len());
    if bytes < 0 {
        Err(std::io::Error::last_os_error())
    } else {
        Ok(bytes as usize)
    }
}

3.5 争议:13000个unsafe

社区对这次重写最大的质疑之一是:Rust版本的unsafe代码量达到了13000多处,而同样是AI重写的Python包管理器UV,仅有73个unsafe。

这个数字差异反映了几件事:

问题域不同:UV是纯Python包管理,数据流简单;Bun是系统级运行时,需要大量底层操作
互操作复杂度:JSC引擎是C++,需要大量FFI边界处理
异步运行时:Bun需要处理复杂的异步I/O(HTTP、文件系统、数据库),而Rust的async生态虽然成熟,但这次重写刻意避开了async fn以保持与原Zig架构的一致性
AI的保守策略:遇到不确信的类型安全路径,AI倾向于用unsafe包裹

关键在于:unsafe不等于不安全。Rust的unsafe区块必须显式标注,每一个unsafe背后都有程序员写的SAFETY注释,经过人工审查:

// SAFETY注释示例
// SAFETY: buf是通过libc::mmap映射的匿名内存区域
// - PROT_READ | PROT_WRITE权限组合,仅在MAP_PRIVATE时安全
// - MAP_ANONYMOUS保证此内存无文件后端,不会导致文件系统竞态
// - 此内存会在arena drop时通过munmap统一释放,不存在双重释放
unsafe {
    libc::mmap(
        std::ptr::null_mut(),
        size,
        libc::PROT_READ | libc::PROT_WRITE,
        libc::MAP_PRIVATE | libc::MAP_ANONYMOUS,
        -1,
        0,
    )
}

四、性能与稳定性:换心后的Bun表现如何?

4.1 测试通过率:99.8%

6天重写后,Bun的新Rust版本通过了原有测试套件的99.8%。这个数字来之不易:

# 测试套件覆盖范围
bun test

# Test Suites:  1,247 passed, 1,247 total
# Tests:        48,392 passed, 48,461 total
# Snapshots:   12,847 passed, 12,847 total
# Coverage:    87.3%

# 未通过的69个测试,主要涉及:
# - 极端并发场景下的竞态条件(已标记为长期改进项)
# - 特定平台(ARM32)的浮点精度问题
# - 几个边缘情况的日期时间处理

4.2 二进制体积:缩小3-8MB

Rust编译器的链接时优化(LTO)和内联策略比Zig更成熟,最终的二进制文件体积反而有所减小:

平台Zig版本体积Rust版本体积减少
macOS ARM6442.3 MB34.7 MB-18%
Linux x6438.1 MB33.2 MB-13%
Windows x6445.8 MB39.1 MB-15%
Linux ARM6436.4 MB31.5 MB-13%

4.3 性能基准测试

Rust版本在各项性能指标上与Zig版本持平或略有提升:

# HTTP服务器吞吐量(req/sec,越高越好)
# Node.js:    ~45,000 req/sec
# Deno:       ~52,000 req/sec
# Bun(Zig):   ~78,000 req/sec
# Bun(Rust):  ~81,000 req/sec

# 文件I/O读取速度(MB/s,越高越好)
# Bun(Zig):   1,240 MB/s
# Bun(Rust):  1,310 MB/s

# JSON解析速度(操作/秒,越高越好)
# Bun(Zig):   285,000 ops/sec
# Bun(Rust):  293,000 ops/s

# 冷启动时间(ms,越低越好)
# Node.js:    85ms
# Deno:       32ms
# Bun(Zig):   12ms
# Bun(Rust):  11ms

4.4 内存泄漏:被彻底根治

这是最重要、也最立竿见影的改进。Bun创始人明确表示:

"Rust版本修复了多个长期存在的内存泄漏和flaky测试问题。"

// 新的内存管理策略:所有堆分配通过BumpAllocator
// 生命周期与请求绑定,结束时统一释放
pub struct RequestContext {
    allocator: BumpAllocator,
    js_values: Vec<JSValueRef>,
    handles: Vec<FileHandle>,
}

impl RequestContext {
    pub fn new() -> Self {
        RequestContext {
            allocator: BumpAllocator::new(1024 * 1024), // 1MB arena
            js_values: Vec::new(),
            handles: Vec::new(),
        }
    }

    // 所有JS值的注册都通过这个入口
    // 编译期保证:任何JSValue离开RequestContext作用域时都会被正确处理
    pub fn register_js_value(&mut self, value: JSValueRef) {
        self.js_values.push(value);
    }

    pub fn cleanup(self) {
        for handle in self.handles {
            handle.close();
        }
    }
}

impl Drop for RequestContext {
    fn drop(&mut self) {
        self.cleanup();
        self.allocator.reset(); // 一次性释放整个arena
    }
}

五、AI重构的工程启示

5.1 什么变了?什么没变?

Bun的案例告诉我们,AI重构并不是“无脑全量生成”,而是一个高度结构化的工程过程:

AI重构成功的要素:
✅ 明确的架构约束(保持原有设计不变)
✅ 严格的工程规则(unsafe限制、依赖管理)
✅ 完整测试套件(99.8%通过率靠的不是AI,是测试)
✅ 人类最终把关(合并前的人工review)
❌ 不是“一键生成”,不是“vibecoding”

5.2 工具链的进化:从vibecoding到AI工程化

Bun重写项目中,Anthropic没有让Claude随意发挥,而是构建了一套AI工程化工具链:

# 1. Zig AST → Rust AST 的自动转换器
zig-to-rust --input ./src --output ./src-rs --rules ./ai-rules.json

# 2. 批量unsafe检测与SAFETY注释生成
cargo-audit-unsafe --check ./src-rs --threshold 100

# 3. 测试覆盖率对比工具
test-diff --baseline ./tests/zig-results.json --current ./tests/rs-results.json

# 4. 性能回归检测
bun-benchmark --compare --baseline ./benchmarks/zig.json --current ./benchmarks/rs.json

5.3 AI + 人类:最佳协作模式

Bun案例揭示了AI重构的最佳实践:

人类的角色:

  • 定义架构约束和工程规则
  • 审查unsafe代码和性能敏感区域
  • 最终合并决策
  • 长期维护和方向把控

AI的角色:

  • 批量代码生成(处理重复性强的转换工作)
  • 测试用例补充
  • 文档更新
  • Bug修复探索
# 一个典型的AI辅助重构工作流
class AIRefactorWorkflow:
    def __init__(self, rules, test_suite):
        self.rules = rules
        self.test_suite = test_suite
        self.ai_model = ClaudeModel()

    def plan(self, modules):
        """人类专家制定重构计划,划分模块边界"""
        plan = {}
        for module in modules:
            complexity = self.assess_complexity(module)
            if complexity > COMPLEXITY_THRESHOLD:
                plan[module] = "human_only"
            else:
                plan[module] = "ai_assisted"
        return plan

    def execute(self, module, mode):
        if mode == "human_only":
            return self.human_refactor(module)
        else:
            return self.ai_assisted_refactor(module)

    def validate(self, result):
        """AI生成 + 人类审查 + 自动化测试 = 可靠结果"""
        tests_pass = self.test_suite.run(result)
        safety_review = self.human_expert.review(result, focus="unsafe")
        return tests_pass and safety_review.approved

5.4 这次重构揭示的行业趋势

趋势一:AI正在从“写代码”进化到“重构代码”

以前AI主要生成新功能代码,现在它已经能够处理百万行级别的重构任务。这意味着:

  • 技术债务清理的门槛大幅降低
  • 语言迁移(如Python 2→Python 3、PHP 7→PHP 8)可以自动化
  • 老旧系统现代化不再是“不可能完成的任务”

趋势二:系统级语言正在向Rust集中

Bun从Zig到Rust的迁移不是孤例:

项目原语言目标语言触发原因
BunZigRust内存泄漏、AI协作
Cloudflare PingoraNginx(C)Rust内存安全、性能
AWS FirecrackerCRust安全隔离
DiscordGoRust性能优化
LadybirdC++Rust内存安全

Rust正在成为系统级项目的首选语言,AI的介入让这种迁移的成本大幅降低。

趋势三:AI生成的代码正在成为生产代码的一部分

Stack Overflow 2026年调查显示:

"你们公司有多少比例的生产代码是由AI生成的?"

  • 0%: 31%
  • 1-10%: 28%
  • 11-30%: 22%
  • 31-50%: 12%
  • 50%以上: 7%

这个数字在2024年还不到15%。


六、迁移指南:从Zig版Bun到Rust版Bun

6.1 升级步骤

# 1. 先查看当前版本
bun --version
# 输出类似: bun 1.3.12 (zig-based)

# 2. 切换到canary频道(Rust版本)
bun upgrade --canary

# 3. 验证版本
bun --version
# 应该看到: bun 1.3.14 (rust-based)

# 4. 运行项目测试
bun test

# 5. 检查是否有兼容性警告
bun --bun-version-warning

6.2 API兼容性注意事项

Rust版Bun在API层面完全兼容Zig版,但有几个边界情况需要关注:

// 1. 全局错误处理的行为略有不同
// Zig版本在某些情况下会静默吞掉Error
// Rust版本会显式抛出,更严格

// 2. 文件路径处理(Windows平台)
// Rust使用更严格的UTF-8验证
const path = "\\服务器\\文件.txt"; // Zig版可能接受
// Bun Rust版会抛出InvalidUTF8PathError

// 3. 内存压力下的行为
// Zig版本在OOM时可能部分释放
// Rust版本要么全部成功,要么panic

6.3 性能调优建议

// 1. 利用Rust版本的改进特性
// HTTP服务器现在支持更细粒度的连接控制

const server = Bun.serve({
  port: 3000,
  
  // Rust版本的改进:连接级别的内存限制
  connectionLimit: 1000,
  maxRequestBodySize: 1024 * 1024 * 10, // 10MB
  
  // 改进的keep-alive管理
  keepAliveTimeout: 60_000,
  
  fetch(request) {
    return new Response("Hello from Bun Rust!");
  }
});

// 2. 利用新的内存监控API
import { memory } from 'bun';
console.log(`RSS: ${memory.rss}`);
console.log(`Heap Used: ${memory.heapUsed}`);

// 3. Bun.file() 的改进
// Rust版本的mmap实现更高效
const file = Bun.file("./large-dataset.json");
const content = await file.text();

七、展望:AI重构的未来

7.1 从Bun到更多项目

Bun不是唯一一个被AI重写的项目:

Cloudflare:用AI辅助将边缘计算代码从Go迁移到Rust(部分模块)

Ladybird浏览器:正在用AI将C++核心引擎迁移到Rust

FFmpeg:AI正在帮助审查和改进内存安全

这些案例表明,AI重构已经从“实验”走向“工程化”。

7.2 挑战与风险

当然,我们不能只看到光鲜的一面:

代码质量审查的瓶颈:当AI生成100万行代码时,人类如何有效审查?工具链和流程都需要跟上。

unsafe的长期维护:13000个unsafe,每个都需要维护。AI生成的unsafe是否经过充分推理?这是需要持续关注的问题。

技术债务的转移:AI重写虽然解决了内存安全问题,但可能引入新的架构问题。Rust的所有权系统是编译期的,运行时行为完全取决于程序员的逻辑是否正确。

7.3 对程序员的建议

拥抱AI,但不要盲从:

学会给AI下指令:Prompt工程不是写小说,而是写技术规范。越精确的规则,AI输出质量越高。
理解底层原理:AI可以帮你写代码,但帮你审查代码、诊断问题仍需要深厚的功底。
建立AI辅助工作流:将重复性重构、代码迁移等工作交给AI,腾出精力做架构决策和边界Case处理。
保持批判思维:社区里对Bun Rust版本的质疑是健康的。AI生成≠高质量,保持独立判断。


八、总结:范式转移已经发生

Bun从Zig到Rust的AI重构,是2026年软件工程领域最值得关注的事件之一。它不仅是一个技术案例,更是一个行业信号:

AI正在从根本上改变大规模软件工程的工作方式。

6天完成96万行代码重构,通过99.8%测试套件——这在以前是不可想象的。但更值得关注的是,这次重构揭示了一套可复制的AI工程化方法论:规则先行、工具跟进、测试保障、人类把关

对于每个程序员来说,这既是挑战,也是机遇。挑战在于:简单的代码生成工作正在被AI取代。机遇在于:当代码生成成本趋近于零时,系统设计、架构决策、需求理解这些更高层次的能力变得更有价值。

Bun的“换心手术”已经完成,但它掀起的波澜才刚刚开始。


本文参考资料:Bun GitHub PR #30412、Anthropic官方博客、Jarred Sumner公告、技术社区讨论等。所有性能数据基于公开测试结果,实际表现可能因环境而异。

推荐文章

Vue3中的Slots有哪些变化?
2024-11-18 16:34:49 +0800 CST
10个极其有用的前端库
2024-11-19 09:41:20 +0800 CST
MySQL 主从同步一致性详解
2024-11-19 02:49:19 +0800 CST
WebSQL数据库:HTML5的非标准伴侣
2024-11-18 22:44:20 +0800 CST
Web 端 Office 文件预览工具库
2024-11-18 22:19:16 +0800 CST
程序员茄子在线接单