编程 Bun 用 Claude 在 6 天内重写 96 万行 Rust:AI 驱动的大规模代码迁移深度解析——从 Zig 到 Rust 的工程实践、unsafe 争议与信任危机

2026-05-29 06:08:35 +0800 CST views 8

Bun 用 Claude 在 6 天内重写 96 万行 Rust:AI 驱动的大规模代码迁移深度解析——从 Zig 到 Rust 的工程实践、unsafe 争议与信任危机

一、一场改写开源软件历史的实验

2026 年 5 月,JavaScript 运行时 Bun 完成了一次史无前例的技术跨越:用 AI 在大约 6 天内将整个核心运行时从 Zig 语言迁移到 Rust,涉及约 96 万行代码、6755 个 commit,现有测试套件通过率 99.8%。这不是概念验证,不是 demo,而是一个拥有 9 万多 GitHub star、被 Claude Code 等生产级工具深度依赖的运行时,直接把 AI 生成的 Rust 代码合入了主分支。

这件事为什么值得每个程序员关注?因为它不只是 Bun 的技术选型问题,更是 AI 重写软件这个范式的一次公开大考。当 AI 可以用 6 天完成人类可能需要数年的跨语言迁移时,速度、质量、信任三者之间的张力,就成了我们必须正视的工程命题。

二、背景:为什么 Bun 要离开 Zig?

2.1 四年 Zig 之路:从明星项目到困境

Bun 自诞生以来,一直是 Zig 生态最成功的代表作。Zig 带来了极致的启动速度、对系统底层的精细控制,以及与 C 的无缝互操作能力。Bun 凭借这些优势,一度成为 Node.js 和 Deno 之外最令人期待的 JavaScript 运行时。

但四年下来,问题开始积累:

内存泄漏成为顽疾。 Bun 的内存问题不是偶发的小 bug,而是系统性缺陷。2026 年 3 月,Claude Code 的 GitHub 仓库收到一个编号 #33453 的 Issue:主进程在约 3 小时的会话中,RSS 内存从 1.7GB 暴涨到 14GB。Issue 作者直接指出泄漏位于 Bun 运行时的 WebKit Malloc 分配器,而非用户空间的 JavaScript 分配。另一份 Issue #11377 更夸张:运行 14 小时后,Claude Code 进程占用 23GB 虚拟内存,143.8% CPU,系统完全卡死。

Issue 积压触目惊心。 波兰数字会员系统公司 Rewardo 的 CTO Wojciech Maj 做过一个对比:Node.js 作为几乎"驱动整个互联网"的运行时,大约有 1700 个 open issues;而用户规模远小于 Node.js 的 Bun,却已经积累了约 4700 个 open issues。这个差距直接反映了项目成熟度和维护能力的差距。

路线图偏移。 Reddit 用户 Xtergo 的吐槽很有代表性:"Bun 的路线图看起来更像是在不断叠加新功能,而不是优先解决稳定性和 Bug 修复问题。如果这些问题继续得不到解决,我怀疑它永远无法达到 Node.js 那种生产级成熟度。"

2.2 Zig 社区的哲学冲突

内存问题只是表面,更深层的裂痕来自 Bun 和 Zig 社区之间的哲学冲突。

Bun 团队此前已经 fork 了 Zig,声称通过引入 LLVM 并行代码生成,debug 编译速度提升了四倍。但这些优化始终无法 upstream 回 Zig 官方。原因很明确:Zig 社区实行极其严格的"no-AI policy"——禁止 AI 生成的 issue、PR 甚至评论。Zig 基金会成员 Loris Cro 公开表示,大量 LLM 贡献只会制造"幻觉 PR"和"垃圾噪音"。

与此同时,Zig 核心开发者批评 Bun fork 中的一些实现"不适合 upstream",例如并行语义分析可能导致非确定性行为,Bun 对 LLVM backend 的模块拆分方向也被认为有误。

这种冲突在 Anthropic 收购 Bun 后显得格外讽刺:Anthropic 是 AI coding 浪潮最激进的推动者之一,Claude Code 又深度依赖 Bun runtime。一边是 Zig 社区全面封禁 AI 生成代码,另一边是 Bun 团队用 Claude agent 大规模把 Zig 迁移出去。这已经不是语言选型问题,而是两种软件工程哲学的正面碰撞。

2.3 Anthropic 收购后的压力

2025 年 12 月,Anthropic 收购了 Bun。官方说法是"加速 Claude Code 能力",本质上 Bun 成了 Claude Code 背后的运行时、包管理器、bundler 和测试工具。

Claude Code 负责人 Boris Cherney 曾解释选择 Bun 的原因:

"Bun 的启动时间大概只有 3 毫秒,而 Python 要慢 15 倍左右。对于 CLI 工具来说,这意味着用户体验是'丝滑响应',还是'明显卡顿'。"

但现实是:Claude Code 以 Bun 可执行文件的形式发布,Bun 的内存泄漏直接成了 Claude Code 的内存泄漏。一个荒诞的循环形成了——Claude Code 被 Bun 的内存泄漏坑惨了,然后 Anthropic 让 Claude 去重写 Bun,Bun 再继续支撑 Claude Code。

三、迁移过程:576 行指南与六天冲刺

3.1 PORTING.md:AI 大规模迁移的工程规范

2026 年 5 月初,Bun 仓库出现了一个名为 claude/phase-a-port 的新分支,同时出现了一份长达 576 行的 PORTING.md 文档。这份文档才是整个迁移的真正灵魂——它不是让 AI 自由发挥,而是极其精细地规定了迁移的每一步:

Phase A:逐文件忠实翻译

  • 逐文件将 Zig 代码翻译为 Rust,忠实保留原始逻辑
  • 即使 Rust 代码暂时不能编译也没关系
  • 文件命名规则:src/bun.js/xxx.zigcrates/bun-js-xxx/src/xxx.rs
  • Crate 引用必须严格按模块边界划分

Phase B:逐 crate 修复编译

  • 逐个 crate 解决编译、构建和运行问题
  • 这个阶段才开始处理 Rust 特有的所有权、生命周期问题

硬性约束:

  • 禁止使用 tokio、rayon、hyper、futures 等 async 生态库
  • 禁止使用 async fn
  • unsafe 必须写明 SAFETY 注释,解释为什么安全
  • 遇到不确定逻辑时,宁可留下 TODO,也不要让 AI 自行猜测

这种设计非常聪明:先让 AI 做"语义投影"——把 Zig 的逻辑 1:1 投射到 Rust,不追求 Rust idiom,只求行为一致。然后再逐步优化。这避免了 AI 在迁移过程中"创造性发挥"带来的风险。

3.2 时间线:从"可能扔掉"到"合并入主分支"

日期事件
5月5日claude/phase-a-port 分支出现,PORTING.md 发布
5月7日Jarred 发推:4000 次 commit、96 万行代码,只剩 3 个编译错误
5月7日Rust 版本已能运行 JavaScript,bun run 和 package.json scripts 可用
5月9日Linux x64 glibc 环境下测试套件通过 99.8%
5月11日Jarred 发推:"如果我们合并 Rust 重写版本,这将是 Zig 的最后一个版本"
5月14日PR #30412 合并入 main:Rewrite Bun in Rust

注意 5 月 7 日 Jarred 在 Hacker News 上的说法:

"整个讨论有点反应过度了。302 条评论,全都围绕一堆根本还跑不起来的代码。我们并没有决定一定要重写。而且这些代码最后被全部扔掉的概率其实非常高。"

六天后,同样的代码变成了"Zig 的最后一个版本"。从"大概率扔掉"到"合并入主分支",只用了不到一周。

3.3 迁移的工作流设计

Jarred Sumner 使用的是 Claude Code 的动态工作流模式,而非简单的对话式编程。具体操作方式是:一个工作流先把 Zig 代码逐文件翻译成 Rust,另一个工作流再逐个 crate 修复编译问题。这种 pipeline 式的自动化流程,让 AI 可以持续不间断地工作,不需要人类在每个步骤之间手动介入。

在 5 月 3 日,Jarred 就曾在推特上预告过:

"这种 pipeline,任何 VC 支持的 OSS 或者有大量 GitHub issues 的公司都能搭建。更普遍地说,它可以用于自动修复用户报告的 bug。"

他还更早预言过:

"我预计开源软件会走向完全相反的方向——未来甚至可能变成'禁止人类贡献代码'。人类依然会负责讨论问题、决定优先级,但真正写代码、提交 PR、回复和处理反馈、完成实现的工作,最终都会由 LLM 来完成。"

四、unsafe 争议:13000 个安全漏洞还是必要的底层接口?

4.1 73 vs 13000:一组刺眼的数字

迁移公布后,最引爆争议的对比来自 t3.gg 创始人 Theo:

  • uv(Astral 出品的 Python 包管理器):35 万行 Rust 代码,73 个 unsafe 调用
  • Bun Rust 移植版:68.1 万行 Rust 代码,超过 13000 个 unsafe 调用

差了接近 180 倍。

Jarred 几乎立刻回应:"今天已经下降了大约 2000。我预计它会稳定在 1 万左右,因为 Bun 的大部分内容都是用 C 和 C++ 编写的,这种情况不会改变。"

4.2 这个对比公平吗?

平心而论,这个对比确实不完全公平。uv 是一个相对纯粹的 Rust 项目,而 Bun 需要与大量底层 C/C++ 组件打交道:

// Bun 必须通过 unsafe 与 JavaScriptCore 交互
unsafe {
    jsc_context_evaluate(context, script_ptr, script_len)
}

// Bun 必须通过 unsafe 调用 libuv 的 C API
unsafe {
    uv_fs_open(loop_ptr, req_ptr, path_ptr, flags, mode, callback)
}

// Bun 必须通过 unsafe 操作 mimalloc 分配器
unsafe {
    mi_malloc_aligned(size, alignment)
}

JavaScriptCore、libuv、mimalloc、uWebSockets——这些都是 C/C++ 写的,Bun 必须通过 FFI(Foreign Function Interface)与它们交互,而 FFI 在 Rust 中天然就是 unsafe 的。

4.3 Bun 官方的 unsafe 审计

5 月 21 日,Bun 官方发布了 unsafe 审计页面,确认了以下数据:

  • Rust port 有 13,365 个 unsafe blocks
  • 约 9,300 个可以转成 safe code
  • 约 4,000 个会保留在必要边界上

官方解释 unsafe 的三大来源:

  1. FFI 边界:与 JavaScriptCore、libuv 等 C/C++ 组件的接口调用
  2. 从 Zig port 过来的 ownership idiom:Zig 的内存管理方式直接映射到 Rust 时,很多地方需要 unsafe 来模拟 Zig 的指针操作
  3. 少量性能路径:热路径上为避免 Rust 的运行时检查而使用 unsafe

4.4 真正的问题不在数量,在可审查性

开发者 Aashish Ranjan Singh 的评论直击要害:

"UV rust 是由真正的开发人员编写的,每一行代码都经过了审查。Bun rust 由 Agents 编写,由 Agents 审核,并由 Agents 批准和合并。"

这才是核心问题。如果每个 unsafe block 都有人类工程师审查过、写过 SAFETY 注释、验证过不变量,那 13000 个也许是合理的。但如果这些 unsafe 是 AI 在"忠实翻译"过程中批量生成的,没有人逐个验证过安全性条件是否真正成立,那每一个 unsafe 都可能是一个定时炸弹。

让我展示一个具体的例子,说明 AI 翻译可能产生的 unsafe 隐患:

// Zig 原始代码:直接指针操作,但 Zig 的安全性保证不同
fn getObject(ptr: [*]const u8, offset: usize) *const Object {
    return @ptrFromInt(@intFromPtr(ptr) + offset);
}
// AI 翻译的 Rust 代码:直接复制了 Zig 的指针逻辑
unsafe fn get_object(ptr: *const u8, offset: usize) -> *const Object {
    // SAFETY: ??? (AI 可能没有验证对齐、非空、生命周期)
    (ptr as *const u8).add(offset) as *const Object
}

Zig 中 @ptrFromInt 是有一定安全保障的(比如 debug 模式下会检查对齐),但 Rust 的裸指针强制转换没有这些检查。如果 AI 只是"忠实翻译"了逻辑而没有理解两种语言在安全性语义上的差异,SAFETY 注释可能就是空的或者不正确的。

4.5 unsafe 审计的正确做法

对 AI 大规模生成的代码做 unsafe 审计,应该包含以下步骤:

第一步:分类标记

// Category 1: FFI boundary - 与 C/C++ 交互(必要的 unsafe)
unsafe fn jsc_evaluate(ctx: *mut JSCContext, script: *const c_char) -> JSValueRef {
    // SAFETY: ctx 由 JSCContext::new 保证有效,script 由调用者保证 \0 结尾
    jsc_context_evaluate(ctx, script, strlen(script))
}

// Category 2: Zig idiom translation - 从 Zig 习惯搬运过来的(可以改为 safe)
unsafe fn get_task_data(task: *mut Task) -> &TaskData {
    // SAFETY: Zig 用 tagged pointer,Rust 可以用 enum
    // TODO: 重构为 enum + Option 消除此 unsafe
    &*((*task).data as *const TaskData)
}

// Category 3: Performance path - 性能优化(需要 benchmark 验证)
unsafe fn fast_hash(data: &[u8]) -> u64 {
    // SAFETY: data 保证有效且对齐,read_unaligned 在对齐时等价于 read
    // Benchmark: 此路径占热点循环 15%,使用 safe 版本性能下降 8%
    data.as_ptr().read_unaligned() as u64
}

第二步:逐个验证 SAFETY 注释

每个 unsafe block 的 SAFETY 注释必须回答:

  1. 哪些不变量(invariant)是调用者必须保证的?
  2. 这些不变量在当前调用上下文中是否真的成立?
  3. 如果不变量被违反,后果是什么?是否会导致未定义行为?

第三步:消除可消除的 unsafe

Bun 官方说 9300 个可以转成 safe。但"可以"不等于"会"。这需要一个系统性的重构计划:

// Before: AI 翻译的 unsafe 代码
unsafe fn handle_event(event: *mut Event) -> EventResult {
    let kind = (*event).kind;
    let data = (*event).data;
    process_event(kind, data)
}

// After: 重构为 safe Rust
fn handle_event(event: &Event) -> EventResult {
    process_event(event.kind, event.data)
}

五、社区反应:信任危机与项目出走

5.1 yt-dlp:第一个正式限制 Bun 的项目

5 月 20 日,视频下载工具 yt-dlp 在 GitHub issue #16766 中宣布:Bun 作为 EJS 兼容 JavaScript runtime 的支持将被限制并弃用。它把支持范围限定到 Bun 1.2.11 到 1.3.14(Zig 版本的最后版本)。

理由有两层:

  1. 早期 Bun 版本构建 EJS 时可能忽略 lockfile,结合近期 npm 供应链攻击背景被认为有安全风险
  2. Bun 最近用 Claude 重写成 Rust,维护者认为其开发方向有"fully vibe-coded"的趋势

5.2 Electrobun:架构核心解耦

Electrobun 原本把 Bun 放在架构核心——官方文档写着"Electrobun app 本质上是一个 Bun app"。5 月 23 日,作者 Yoav 宣布 Electrobun 2.0 将因 Rust rewrite 与 Bun 解耦。

这是一个非常重大的信号:当你的架构核心组件的底层实现被 AI 在 6 天内重写,你不知道下个版本会不会又来一次"换心手术",这种不确定性对下游项目来说是致命的。

5.3 其他迁移动态

项目动作时间与 Rust 重写的关系
Sentry CLI移除 Bun artifacts,迁移到 Node.js5月22日未明确提及
OpenTUI移除 Bun-specific runtime dependencies5月6日发生在合并前
Variableland/dx从 Bun runtime 迁到 Node.js5月1日发生在合并前

准确说法:至少 yt-dlp 和 Electrobun 的解耦是直接回应 Rust 重写事件;其他项目更多是 runtime-agnostic 的趋势,不能都归因于同一事件。

六、技术深度:Zig 到 Rust 迁移的工程挑战

6.1 内存模型差异

Zig 和 Rust 的内存管理哲学完全不同,这是迁移中最核心的挑战。

Zig 的方式:手动管理 + 编译器检查

const std = @import("std");

fn processBuffer(allocator: std.mem.Allocator) ![]u8 {
    // 显式分配,调用者负责释放
    var buf = try allocator.alloc(u8, 1024);
    // Zig 没有 borrow checker,但编译器会在 debug 模式检测一些问题
    return buf;
}

Rust 的方式:所有权 + 借用检查

fn process_buffer() -> Vec<u8> {
    // 所有权自动管理,编译器保证内存安全
    let mut buf = vec![0u8; 1024];
    buf
}

当 AI "忠实翻译" Zig 代码时,最常见的做法是:

// AI 翻译:用 unsafe 绕过 borrow checker
unsafe fn process_buffer(allocator: &mut Allocator) -> *mut u8 {
    let buf = allocator.alloc(1024);
    // 直接返回裸指针,跳过所有权检查
    buf
}

这就是为什么会有 13000 个 unsafe——因为 AI 在翻译时选择了最保守的策略:保持 Zig 的指针语义,用 unsafe 绕过 Rust 的安全检查。这在短期内可以让代码"跑起来",但长期来看,这些 unsafe 块就是技术债。

6.2 Tagged Pointer 的困境

Bun 大量使用 tagged pointer 来处理 event loop task、进程退出回调、非阻塞文件 I/O 等接口。Jarred 在 X 上专门请教 Rust 社区:

"Bun 原来的 Zig 代码大量使用 tagged pointer 来处理 event loop task、进程退出回调、非阻塞文件 I/O 等接口;迁到 Rust 后,如果直接用 trait 或函数指针,可能会带来额外开销。"

Tagged pointer 在 Zig 中的使用:

// Zig: 用指针低位存储类型标记
const Task = struct {
    tagged_ptr: usize, // 低位 2 bits 作为类型标记
    
    fn getType(self: *const Task) TaskType {
        return @enumFromInt(self.tagged_ptr & 0x3);
    }
    
    fn getData(self: *const Task) *align(1) TaskData {
        return @ptrFromInt(self.tagged_ptr & ~@as(usize, 0x3));
    }
};

Rust 中的安全替代方案:

// 方案1: 用 enum 替代 tagged pointer(安全但可能有额外开销)
enum Task {
    FileRead(Arc<FileReadData>),
    Timer(Arc<TimerData>),
    Callback(Arc<CallbackData>),
}

// 方案2: 使用 NonNull + 标记位(仍然是 unsafe,但更规范)
use std::ptr::NonNull;

struct TaggedPtr(NonNull<TaskData>);

impl TaggedPtr {
    // SAFETY: 调用者保证 ptr 非空且对齐到 4 字节
    unsafe fn new(ptr: NonNull<TaskData>, tag: u8) -> Self {
        let addr = ptr.as_ptr() as usize;
        debug_assert_eq!(addr & 0x3, 0, "pointer must be 4-byte aligned");
        TaggedPtr(NonNull::new_unchecked((addr | (tag as usize)) as *mut TaskData))
    }
    
    fn tag(&self) -> u8 {
        (self.0.as_ptr() as usize & 0x3) as u8
    }
    
    fn data(&self) -> NonNull<TaskData> {
        unsafe {
            let addr = self.0.as_ptr() as usize & !0x3;
            NonNull::new_unchecked(addr as *mut TaskData)
        }
    }
}

方案 1 更安全,但有内存布局开销(enum discriminant + padding);方案 2 保持了 Zig 的性能特征,但需要 unsafe。在性能敏感的 runtime 底层,通常选择方案 2,但必须严格验证 SAFETY 条件。

6.3 错误处理模型差异

Zig 使用 error union,Rust 使用 Result。表面相似,但语义不同:

// Zig: error union 是值类型,零开销
fn readFile(path: []const u8) ![]u8 {
    const file = try std.fs.cwd().openFile(path, .{});
    defer file.close();
    return try file.readToEndAlloc(allocator, max_size);
}
// Rust: Result 有运行时开销(但通常很小)
fn read_file(path: &str) -> Result<Vec<u8>, std::io::Error> {
    let mut file = std::fs::File::open(path)?;
    let mut buf = Vec::new();
    file.read_to_end(&mut buf)?;
    Ok(buf)
}

AI 在翻译时,经常直接把 try 映射为 ?,把 ! 映射为 Result<_, Box<dyn Error>>。但 Zig 的 error union 是穷举的——编译器知道所有可能的错误类型;Rust 的 Box<dyn Error> 是类型擦除的——你丢失了错误类型的编译时信息。

6.4 构建系统迁移

Bun 原来使用 Zig 的构建系统,迁移到 Rust 后需要使用 Cargo。这带来几个挑战:

  1. 依赖管理:Zig 没有包管理器,Bun 手动管理所有 C/C++ 依赖;Cargo 有 crates.io,但需要为每个 C/C++ 依赖编写 -sys crate
  2. 交叉编译:Zig 的交叉编译开箱即用,Rust 需要配置 target 和 linker
  3. 构建速度:Rust 的编译速度是出了名的慢,这对开发体验影响很大

七、性能对比:Rust 版本真的更快吗?

7.1 官方公布的数据

Bun 官方在 PR #30412 中声称:

  • 二进制文件体积缩小 3-8 MB
  • 性能测试在各个平台上均达到或超越原有水平
  • 修复了多个长期存在的内存泄漏和 flaky 测试问题

7.2 需要独立验证的指标

作为一个被 AI 在 6 天内重写的运行时,以下指标需要独立的、可复现的基准测试:

启动时间:

# Zig 版本
hyperfine --warmup 3 'bun-zig --version'

# Rust 版本
hyperfine --warmup 3 'bun-rust --version'

Bun 最大的卖点之一是 3ms 的启动时间。如果 Rust 版本在这个指标上有任何退步,对 Claude Code 等 CLI 工具的用户体验影响将是致命的。

内存占用:

# 长时间运行的内存测试
/usr/bin/time -v bun-zig run long-running-script.js
/usr/bin/time -v bun-rust run long-running-script.js

这是 Rust 重写最核心的改进目标——如果长时间运行后内存仍然暴涨,那重写的意义就大打折扣。

包安装速度:

hyperfine --warmup 1 'bun-zig install'  # cold cache
hyperfine --warmup 1 'bun-rust install'  # cold cache

7.3 可能的性能影响分析

从技术角度分析 Rust 重写可能带来的性能影响:

正面因素:

  • Rust 编译器的优化后端(LLVM)比 Zig 的自研后端更成熟
  • Rust 的 monomorphization 可以产生更特化的代码
  • Rust 的类型系统允许编译器做更激进的优化

负面因素:

  • 13000 个 unsafe 块中,相当一部分是为了绕过 borrow checker,可能引入 Rust 编译器无法检查的 UB
  • "忠实翻译"策略意味着代码结构可能不是最优的 Rust 写法
  • 缺少 async runtime 可能导致事件循环的实现不如 Zig 版本自然

八、AI 驱动代码迁移的方法论

8.1 Bun 迁移给我们的启示

Bun 的 PORTING.md 提供了一个可复用的 AI 大规模代码迁移方法论:

Phase A: 语义投影

  1. 逐文件翻译,不追求 idiom
  2. 允许编译失败,优先保证逻辑完整
  3. 严格禁止创造性发挥——AI 不能"优化"或"重构"原始逻辑
  4. 不确定的逻辑留 TODO,不猜测

Phase B: 逐模块修复

  1. 逐个 crate 解决编译问题
  2. 逐步引入目标语言的惯用写法
  3. 通过测试验证行为一致性

关键原则:

  • 速度来自 pipeline 自动化,不是牺牲质量
  • 约束 AI 的自由度比提高 AI 的能力更重要
  • 576 行的迁移指南说明:人类的核心价值在于定义规则和边界,AI 的价值在于执行

8.2 可复用的迁移脚本框架

基于 Bun 的经验,我们可以设计一个通用的 AI 驱动代码迁移框架:

# migration_framework.py
import subprocess
import json
from pathlib import Path

class MigrationFramework:
    def __init__(self, source_dir: str, target_dir: str, rules: dict):
        self.source_dir = Path(source_dir)
        self.target_dir = Path(target_dir)
        self.rules = rules
        self.results = {"translated": 0, "errors": 0, "todos": 0}
    
    def phase_a_translate(self):
        """Phase A: 逐文件忠实翻译"""
        source_files = sorted(self.source_dir.rglob("*.zig"))
        
        for src_file in source_files:
            relative = src_file.relative_to(self.source_dir)
            target_file = self.target_dir / relative.with_suffix(".rs")
            
            prompt = self._build_translation_prompt(src_file)
            result = self._call_ai(prompt)
            
            # 验证翻译结果
            self._validate_translation(src_file, result, target_file)
            
            # 统计 TODO 数量
            todo_count = result.count("TODO")
            self.results["todos"] += todo_count
            
            target_file.parent.mkdir(parents=True, exist_ok=True)
            target_file.write_text(result)
            self.results["translated"] += 1
    
    def phase_b_fix_compilation(self):
        """Phase B: 逐 crate 修复编译"""
        # 尝试编译每个 crate
        crates = list(self.target_dir.glob("**/Cargo.toml"))
        for crate_dir in crates:
            result = subprocess.run(
                ["cargo", "check"],
                cwd=crate_dir.parent,
                capture_output=True,
                text=True
            )
            
            if result.returncode != 0:
                errors = self._parse_rust_errors(result.stderr)
                for error in errors:
                    self._fix_compilation_error(crate_dir.parent, error)
    
    def _build_translation_prompt(self, source_file: Path) -> str:
        return f"""Translate this Zig file to Rust following these rules:
1. Preserve all logic exactly, do not optimize or refactor
2. Use unsafe where necessary with SAFETY comments
3. Leave TODO for uncertain logic, never guess
4. File mapping: src/xxx.zig -> crates/xxx/src/xxx.rs
5. Do NOT use tokio, rayon, hyper, futures, or async fn
6. Every unsafe block must have a SAFETY comment

Source file: {source_file.name}
Content:
{source_file.read_text()}"""
    
    def _validate_translation(self, src, result, target):
        """基本验证:检查是否有遗漏的函数"""
        # 提取源文件中的函数名
        source_fns = set(re.findall(r'fn\s+(\w+)', src.read_text()))
        # 提取翻译结果中的函数名
        target_fns = set(re.findall(r'fn\s+(\w+)', result))
        # 检查遗漏
        missing = source_fns - target_fns
        if missing:
            print(f"WARNING: Missing functions in {target.name}: {missing}")
    
    # ... 其他方法省略

8.3 迁移后的质量保证流程

AI 生成的代码需要比人类代码更严格的质量保证:

  1. 双重审查:AI 生成的代码至少需要两名人类工程师审查
  2. 差分测试:对相同输入,Rust 版本和 Zig 版本的输出必须完全一致
  3. 模糊测试:对 FFI 边界做密集模糊测试,发现 unsafe 可能引入的问题
  4. 内存安全测试:使用 Miri(Rust 的未定义行为检测工具)检查 unsafe 代码
  5. 渐进式发布:先 canary,再 stable,每个阶段都做充分验证
# 使用 Miri 检查 unsafe 代码
cargo miri test

# 使用 AddressSanitizer 检测内存错误
RUSTFLAGS="-Z sanitizer=address" cargo test --target x86_64-unknown-linux-gnu

# 使用差分测试对比 Zig 和 Rust 版本的输出
diff <(bun-zig run test.js) <(bun-rust run test.js)

九、AI 重写软件的更广泛趋势

9.1 不是孤例

Bun 的六天重写虽然最引人注目,但类似的 AI 驱动极限重写正在多个领域同时发生:

  • Cloudflare 在一周内借助 AI 重新实现了 Next.js API 的大部分能力
  • Ladybird 浏览器 在两周内将自己的 JavaScript 引擎从 C++ 迁移到了 Rust
  • Vercel ZeroNative 在 Bun 迁移 Rust 的同时,反其道而行用 Zig 构建原生应用框架

9.2 速度与信任的博弈

AI 大规模改写基础设施时最容易被忽略的一点:AI 可以快速制造"行为相似"的代码,但基础设施项目需要的不只是行为相似。它还需要:

  • 可解释的设计:为什么做了这个架构选择?AI 生成的代码很难回答这个问题
  • 可追踪的历史:git blame 要能找到责任人和原因,而不是"AI 生成的"
  • 可审查的变更:每个变更的 diff 必须是人类可理解的
  • 出事故时的可定位性:人类维护者必须能快速定位问题所在

9.3 "AI 写、AI 审、AI 合"的危险

当整个流程变成 AI 写代码、AI 审查代码、AI 合并代码时,就出现了一个问题:谁来为代码质量负责?

在传统软件工程中,代码审查是知识传递和责任确认的关键环节。审查者不只是检查代码有没有 bug,更是在确认"我理解这段代码在做什么,我愿意为它的正确性背书"。当审查者也是 AI 时,这个背书就变得毫无意义——因为 AI 不为任何后果负责。

十、实践建议:如果你的项目也在考虑 AI 重写

10.1 什么情况下适合 AI 重写

  • 代码库有完善的测试套件(覆盖率 > 90%)
  • 目标语言和源语言的语义映射相对直接
  • 项目有足够的资源做长期维护
  • 重写后可以并行运行两个版本进行对比

10.2 什么情况下不适合

  • 核心基础设施组件,下游依赖众多
  • 测试覆盖率不足,无法验证行为一致性
  • 涉及复杂的内存安全或并发逻辑
  • 团队没有目标语言的深度经验

10.3 如果你的项目正在用 Bun

不要恐慌式删除。Bun 1.3.14 仍然是原 Zig 实现,官方也说 Rust port 还没进正式 release。更理性的做法是按用途分层看待:

包管理器用途(风险低):

  • 关注 lockfile 一致性
  • 确保 CI 可复现
  • 做好供应链扫描

测试运行器用途(风险中):

  • 确认测试语义与 Node/Vitest/Jest 的差异
  • 对关键路径做交叉验证

生产运行时用途(风险高):

  • 谨慎使用 Bun-only API
  • 尤其注意 FFI、HTTP server、数据库、子进程、文件系统等靠近系统边界的能力
  • 准备好 Node/Deno 降级方案

外部用户安装用途(风险最高):

  • 不要把 Bun 作为唯一可用 runtime
  • 至少提供 Node/Deno 路径

十一、总结与展望

Bun 的 Rust 重写是一次极具争议的技术实验,它同时展示了 AI 驱动代码迁移的巨大潜力和严重风险:

潜力:

  • 6 天完成 96 万行代码的跨语言迁移,这是人类不可能企及的速度
  • 576 行的 PORTING.md 证明:好的规则比好的 AI 更重要
  • 99.8% 的测试通过率说明 AI 可以做到行为级别的兼容

风险:

  • 13000 个 unsafe 块中,多少有正确的 SAFETY 注释?
  • "AI 写、AI 审、AI 合"的模式让责任归属变得模糊
  • 项目出走(yt-dlp、Electrobun)说明信任损失是真实的经济成本

关键教训:

  1. AI 是加速器,不是替代品——它可以让你更快地到达目的地,但不能替你判断方向对不对
  2. 速度上天,信任落地——代码可以 6 天写完,但信任需要数月甚至数年建立
  3. 规则比能力重要——576 行的迁移指南才是这次重写最有价值的资产
  4. 基础设施需要的不只是正确性——还需要可解释性、可审查性、可维护性

未来,当你的 CTO 说"我们要把代码库从 X 语言重写成 Y 语言"时,他不会再问"需要几个月",而是会问"Claude 需要几天"。但请记住:AI 可以加速迁移,不能替代信任建设。对底层工具来说,最贵的是让别人相信这段代码值得运行在自己的机器上。


选题来源: Bun Rust重写审计 unsafe

参考资料:

  • Bun PR #30412: Rewrite Bun in Rust
  • Bun unsafe 审计页面: Bun's unreleased Rust port has 13,365 unsafe blocks
  • Claude Code Issue #33453: Memory leak in WebKit Malloc
  • yt-dlp Issue #16766: Bun support limited and deprecated
  • Electrobun 2.0 decoupling announcement
  • Heise: AI Porting - Claude Rewrites Bun Codebase in Rust
  • The Register: Bun posts Rust porting guide, says rewrite is still half-baked
复制全文 生成海报 Bun Rust Zig AI编程 代码迁移 unsafe Claude Code

推荐文章

JavaScript设计模式:桥接模式
2024-11-18 19:03:40 +0800 CST
如何配置获取微信支付参数
2024-11-19 08:10:41 +0800 CST
Vue3中如何处理WebSocket通信?
2024-11-19 09:50:58 +0800 CST
小技巧vscode去除空格方法
2024-11-17 05:00:30 +0800 CST
MySQL 1364 错误解决办法
2024-11-19 05:07:59 +0800 CST
10个几乎无人使用的罕见HTML标签
2024-11-18 21:44:46 +0800 CST
使用 Git 制作升级包
2024-11-19 02:19:48 +0800 CST
Go中使用依赖注入的实用技巧
2024-11-19 00:24:20 +0800 CST
Linux 网站访问日志分析脚本
2024-11-18 19:58:45 +0800 CST
程序员茄子在线接单