编程 Bun 六天重生:当 AI Agent 用 Rust 重写自己的基础设施——96万行代码的自我颠覆纪实

2026-06-19 15:29:03 +0800 CST views 11

Bun 六天重生:AI Agent 如何用 Rust 重写自己的基础设施——从 Zig 的最后一版到 96 万行代码的自我颠覆

引言:一条推文宣告一个时代的终结

2026年5月11日,Bun 创始人 Jarred Sumner 在 X 上发了一条极其简短的推文:

"Bun v1.3.14 将于明日发布。如果我们合并 Rust 重写版本,这将是 Zig 的最后一个版本。"

就这么一句话。四年前,Bun 因为选择了当时名不见经传的 Zig 语言而显得格外特立独行,被业界视为 Zig 生态最成功的"活广告"——一个用 Zig 写就的生产级 JavaScript 运行时、Node.js 兼容层和打包工具;四年后,这个"广告"亲手宣告了它的语言基座的终结。

但真正让整个技术圈震惊的,不是"Zig 被抛弃"这个事实本身,而是这个切换的速度和质量:

  • 6天——从零到可合并状态
  • 96万行——完全等量替换,不差一行
  • 99.8%测试通过率——几乎零降级
  • 13000+ 个 unsafe——也一并打包带走

而完成这一切的,不是传统意义上的工程师团队接力赛,而是一个 AI Agent。Anthropic 收购 Bun 后,Claude Code 因为 Bun 自身的内存泄漏问题严重影响使用体验,于是 Anthropic 让 Claude 去重写 Bun——让 AI 去修复自己的基础设施,这个闭环本身就充满了科幻感。

本文将深入解析这场 Rust 重写的完整技术脉络:为什么需要重写?迁移架构如何设计?Rust 代码质量究竟如何?13000 个 unsafe 背后是什么考量?Phase B 阶段如何治理?以及这次事件对整个行业的深层启示。


一、背景:被内存泄漏拖垮的 Claude Code

1.1 Bun 在 Claude Code 中的不可替代地位

要理解这次重写,必须先理解 Bun 在 Claude Code 中的深度嵌入。

Claude Code 是以 Bun 可执行文件的形式发布的。当你通过 npm install -g @anthropic-ai/claude-code 安装 Claude Code 时,你实际上同时在运行 Claude Code 的上层逻辑和 Bun 运行时。Bun 不只是"被用到",而是 Claude Code 的统一底层:运行时、包管理器、bundler、测试工具——全部由 Bun 提供。

这种深度嵌入意味着:Bun 的任何性能问题、稳定性问题,都会直接传导到 Claude Code 的终端用户体验上,没有任何隔离层可以缓冲。

Anthropic Claude Code 负责人 Boris Cherney 曾在官方视频中解释过选择 Bun 的技术逻辑:

"我们当初在开发 Claude Code 时,评估了很多运行时方案,Bun 几乎是毫无悬念的胜者。它的启动时间大概只有 3 毫秒,而 Python 要慢 15 倍左右。对于 CLI 工具来说,这意味着用户体验是'丝滑响应',还是'明显卡顿'——没有中间地带。"

从数字来看:3ms 的启动时间,对比 Python 的 ~50ms、Node.js 的 ~15ms,Bun 在冷启动场景下确实具有压倒性优势。对于 Claude Code 这样需要频繁 fork 子进程、执行单文件脚本、启动测试套件的 CLI 工具,这个差距不是"好一点点",而是"能用 vs 不能用"。

1.2 内存泄漏的完整时间线

2026年3月,一个编号 #33453 的 Issue 被提交到 Claude Code 仓库,揭开了这场危机的序幕:

"Claude Code 的主进程表现出严重的内存泄漏,RSS 内存在约 3 小时的短会话中从约 1.7GB 增长到 14GB 以上。泄漏位于 Bun 运行时的 WebKit Malloc 分配器中,而非用户空间的 JavaScript 分配。"

这是一个非常精确的描述:"WebKit Malloc 分配器"——不是 JavaScript 堆泄漏,而是更底层的 C 层面内存问题。这让问题变得尤为棘手,因为 JavaScript 的垃圾回收机制完全无法触及这个层面。

另一个更夸张的记录出现在 Issue #11377(被 bot 标记为重复后关闭):

"运行 14 小时后,Claude Code 进程占用 23GB 虚拟内存,143.8% CPU,系统完全卡死。"

换算一下:Claude Code 在一个工作日内,内存从初始的约 1.7GB 膨胀到 23GB——增长了超过 13倍,而这个增长是线性的、不可逆的,直到进程 OOM 或系统冻结。

1.3 Bun 团队的应对与困境

Bun 团队在 2026年4月发布了 v1.1.13,官方宣称通过更换内存分配器让内存占用下降了 5%。

但社区并不买账。Reddit 用户 Xtergo 的评论很有代表性:

"任何新运行时都会有成熟度问题,这些问题最终会随着时间慢慢被修复。但我担心的是,Bun 的路线图看起来更像是在不断叠加新功能,而不是优先解决稳定性和 Bug 修复问题。Bun 现在已经变得非常复杂了。如果这些问题继续得不到解决,我怀疑它永远无法达到 Node.js 那种生产级成熟度。"

波兰数字会员系统公司 Rewardo 的 CTO Wojciech Maj 做了一个发人深省的数据对比:Node.js 驱动着几乎整个互联网,目前约 1700 个 open issues;而更年轻、用户规模远小于 Node.js 的 Bun,却已经积累了 约 4700 个 open issues

这个对比揭示了一个残酷的现实:在问题积累速度上,Bun 远超 Node.js——而 Node.js 可是承担着全球互联网级别工作负载的项目。这意味着 Bun 团队不是在"正常地修复问题",而是在"加速落后"。

1.4 根本原因:WebKit Malloc 与 JavaScriptCore 的底层交互

要理解为什么 Bun 的内存问题如此顽固,我们需要深入到它的底层架构。

Bun 的 JavaScript 引擎使用的是 JavaScriptCore(WebKit 的 JS 引擎),而不是 V8。JavaScriptCore 与 WebKit Malloc 之间有复杂的交互模式:

// Bun 底层的内存管理交互示意(非实际代码)
// WebKit Malloc (malloc_zone_t) 负责底层的内存分配
// JavaScriptCore 在其上层管理 JS 堆

// 问题场景:JSC 的 GC 堆与 WebKit Malloc 之间的碎片化
// 当 JSC 释放大量对象时,底层分配器可能保留大量内存(slab 分配)
// 导致表面上"已释放"的内存实际上并未归还操作系统

Bun 的前身选择 Zig,部分原因就是 Zig 对底层内存控制有更强的表达能力——但这种控制能力反过来也让问题更难排查:内存泄漏可能发生在 Zig 代码、WebKit Malloc、JSC 引擎的任意一层交互点上。


二、哲学裂痕:Zig 社区的"禁 AI"政策与 Bun 的 AI 大迁移

2.1 为什么 Bun 当年选择了 Zig

Bun 选择 Zig 并非一时冲动。Zig 在 2022 年的吸引力是真实的:

  • 无隐藏控制流:没有隐式的析构函数调用,没有魔术般的错误处理展开
  • 无隐藏内存分配@embedFile 和显式 allocator 让一切内存操作透明
  • ** comptime 机制**:编译期求值能力,类似宏但更安全
  • 与 C 的无缝互操作:可以直接包含 C 头文件并调用 C 函数

这些特性对于实现一个高性能 JavaScript 运行时来说是理想的——你既需要接近 C 的性能,又需要一个现代语言的工程体验。Zig 的"没有 hidden stuff"哲学,对于需要精确控制内存布局和系统调用的 Bun 来说,是完美的匹配。

2.2 Zig 社区与 AI 贡献的根本分歧

然而,Zig 社区有一个鲜明的立场:全面禁止 AI 生成代码

Zig 基金会成员 Loris Cro 公开表示,大量 LLM 贡献只会制造"幻觉 PR"、"垃圾噪音",以及动辄上万行、根本无法维护的提交——而且这些问题会浪费社区宝贵的 review 资源。

Zig 核心开发者对 Bun fork 的批评更具体:

"Bun fork 中的一些实现不适合 upstream。例如,并行语义分析可能导致非确定性行为;Bun 对 LLVM backend 的模块拆分方向也是错误的。"

这种冲突在 Anthropic 收购 Bun 后变得尤为尖锐。Anthropic 本身是 AI coding 浪潮最激进的推动者之一,Claude Code 是其最重要的产品——而 Claude Code 深度依赖 Bun runtime。现在 Anthropic 要让 Claude 去重写 Bun,逻辑上是自洽的,但与 Zig 社区的价值观形成了根本对立。

2.3 一个讽刺的闭环

所以我们看到了一个充满讽刺意味的技术闭环:

Zig 社区:禁止 AI 生成代码
    ↓
Bun 团队需要 AI 来加速开发 → Fork Zig
    ↓
Anthropic 收购 Bun
    ↓
Claude Code 因 Bun 内存泄漏而受影响
    ↓
Anthropic 让 Claude 重写 Bun(大量 AI 生成代码)
    ↓
Rust 版本的 Bun 上线
    ↓
但 Rust 版本的 unsafe 数量是 13000+

这个闭环的核心矛盾在于:Rust 作为一种"内存安全"语言,被引入来解决 Zig 的"内存不安全"问题——但 Rust 版本的 unsafe 数量(13000+)却远超同类项目。这不是说 Rust 本身有问题,而是 AI 生成代码的审查缺失导致了质量隐患。


三、迁移架构:一份576行的 AI 迁移指南设计

3.1 为什么不能直接让 AI 盲目翻译

直觉上,让 AI 重写一个 96 万行的代码库,只需要"把每个 Zig 文件翻译成 Rust"就行了。但这种直觉是错误的——直接翻译会产生大量语义不一致、性能退化、架构混乱的代码。

问题在于:Zig 和 Rust 虽然都是系统编程语言,但它们在以下方面的差异是根本性的:

  1. 错误处理:Zig 用 !T 的 error union,Rust 用 Result<T, E>
  2. 泛型系统:Zig 用 anytypecomptime,Rust 用 trait 和 monomorphization
  3. 并发模型:Zig 单线程优先,Rust 有完整的 async/await 生态
  4. 生命周期:Zig 没有 Rust 的生命周期系统,需要手动管理借用
  5. 内存分配:Zig 的显式 allocator vs Rust 的 std::alloc

如果让 AI 直接翻译而没有指导,AI 很可能选择"字面等效"而非"语义等效"的方案——代码能跑,但性能差、可维护性差。

3.2 PORTING.md:两阶段迁移策略

Bun 团队准备的 PORTING.md(576行)将迁移拆分为严格的两阶段:

Phase A:语义忠实迁移(Semantic Translation)

# Phase A 核心规则

## 目标
逐文件将 Zig 代码翻译为 Rust,不做优化,不做架构变更。

## 优先级
1. 功能正确性 > 性能
2. 可编译 > 代码美观
3. 留下 TODO > 自行猜测逻辑

## 禁止事项
- 禁止引入 tokio、rayon、hyper、futures(Phase A)
- 禁止使用 async fn(Phase A)
- 禁止重构既有逻辑
- 禁止删除代码(即使是"无用"代码)

## Unsafe 规则
- 所有 unsafe 块必须标注 SAFETY 注释
- SAFETY 注释必须说明:什么不变量被假设了?调用者需要满足什么条件?
- 如果无法确定 SAFETY 条件,写 "SAFETY: unknown - requires investigation"

## 文件命名
- Zig: `src/foo/bar.zig`
- Rust: `src/foo/bar.rs`
- 保持完全一致的目录结构

这个 Phase A 策略的本质是:把 AI 翻译质量风险隔离在第一阶段。Phase A 不追求优雅,只追求"能跑且行为一致"。代码丑陋一点没关系,unsafe 多一点没关系——重要的是要通过既有测试套件。

Phase B:Rust 化重构(Rustification)

# Phase B 核心规则

## 目标
在 Phase A 基础上,逐步引入 Rust 惯用法,提升代码质量。

## 可用工具
- tokio: 用于 async I/O
- rayon: 用于数据并行
- 自定义 unsafe wrapper: 用 safe API 包裹 FFI 边界

## 禁止事项
- 不要为了"Rust 风格"而破坏既有行为
- 不要引入可能导致行为变化的优化

## Unsafe 治理
- 每个 unsafe 必须有对应的 safe wrapper
- unsafe 应尽可能集中在 FFI 边界,不应散落在业务逻辑中
- 目标:将 unsafe 数量降至 8000 以下

3.3 迁移进度的里程碑

时间Commit 数代码行数状态
5月初~1000~30万行Phase A 启动
5月7日~4000~96万行剩余 3 个编译错误
5月9日~5000+~96万行Linux x64 测试 99.8% 通过
5月11日--宣布合并意向

最令人震惊的是 5月7日的数据:~4000 个 commits,覆盖 96 万行代码,只剩 3 个编译错误。按 Jarred 的原话:

"按代码量来看,这个说法准确。"

也就是说,这 96 万行代码的绝大部分是在 5月3日到5月7日之间生成的——大约 4 天,4000 个 commits,平均每天 1000 个 commit。这不是传统意义上的软件开发节奏,这是工业化的代码生成。


四、Tagged Pointer 迁移:Event Loop 的底层重构

4.1 Zig 中的 Tagged Pointer 实现

Bun 的 event loop 是其高性能的关键组件之一。Zig 版本大量使用 tagged pointer 来编码任务类型:

// Zig 版本的 tagged pointer:指针低位存储类型标签
const TaskTag = enum(u3) {
    Normal = 0,
    Timeout = 1,
    Io = 2,
    ProcessExit = 3,
};

pub const Task = struct {
    // 将类型标签编码到指针的高位(或低位)几位
    // 由于 Rust/Zig 中的指针都有足够的对齐,我们知道指针的某些低位永远是 0
    raw: usize,

    pub fn create(comptime tag: TaskTag, ptr: anytype) *Task {
        const addr = @intFromPtr(ptr);
        // 将 tag 左移到高位,保留地址空间
        return @ptrFromInt(addr | (@as(usize, @intFromEnum(tag)) << 63));
    }

    pub fn getTag(self: *Task) TaskTag {
        const tag_bits = (self.raw >> 63) & 0x7;
        return @enumFromInt(tag_bits);
    }

    pub fn getPtr(self: *Task, comptime T: type) *T {
        const addr = self.raw & ((1 << 63) - 1);
        return @ptrFromInt(addr);
    }
};

这个设计利用了 Zig 对指针的完全控制权。在 Zig 中,指针永远对齐到至少 2 字节,因此最低位永远是 0——这意味着我们可以用这几位来存储额外信息,而不会丢失真实的地址。

4.2 Rust 中的等价实现

Rust 没有 Zig 的 comptime,但有更强大的类型系统。几种等价方案:

方案一:Enum + NonNull(推荐:类型安全 + 性能兼顾)

use std::ptr::NonNull;

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TaskKind {
    Normal,
    Timeout,
    Io,
    ProcessExit,
}

#[repr(C)]
struct TaskInner {
    // 数据指针
    data: NonNull<()>,
    // 元数据(可扩展)
    metadata: u64,
}

pub struct Task {
    inner: TaskInner,
    kind: TaskKind,
}

impl Task {
    /// 创建新任务,将 kind 信息编码到 metadata 中
    pub fn new(kind: TaskKind, data: NonNull<()>) -> Self {
        Self {
            inner: TaskInner {
                data,
                metadata: 0,
            },
            kind,
        }
    }

    pub fn kind(&self) -> TaskKind {
        self.kind
    }

    pub fn data_ptr<T>(&self) -> NonNull<T> {
        // SAFETY: caller must ensure T is the correct type for this task
        unsafe { NonNull::new_unchecked(self.inner.data.as_ptr() as *mut T) }
    }

    /// 带超时的任务创建
    pub fn with_timeout(data: NonNull<()>, timeout_ms: u64) -> Self {
        let mut task = Self::new(TaskKind::Timeout, data);
        task.inner.metadata = timeout_ms;
        task
    }

    pub fn timeout_ms(&self) -> Option<u64> {
        if self.kind == TaskKind::Timeout {
            Some(self.inner.metadata)
        } else {
            None
        }
    }
}

方案二:Enum + 内存布局(Rust 的完美替代)

// 使用 Rust 的 `#[repr]` 属性精确控制内存布局
#[derive(Debug)]
#[repr(u64)] // 明确使用 64 位整数存储
enum RawTask {
    Normal(*mut ()),
    Timeout {
        ptr: *mut (),
        expires_at_ns: u64,
    },
    Io {
        fd: i32,
        ptr: *mut (),
    },
    ProcessExit(i32),
}

impl RawTask {
    fn ptr(&self) -> *mut () {
        match self {
            RawTask::Normal(p) => *p,
            RawTask::Timeout { ptr, .. } => *ptr,
            RawTask::Io { ptr, .. } => *ptr,
            RawTask::ProcessExit(_) => std::ptr::null_mut(),
        }
    }
}

4.3 性能权衡:为什么 Rust 方案更复杂

Rust 的 enum 是 tagged union,编译器会在运行时存储一个 discriminant(类型标签)。对于 TaskKind 这个只有 4 种变体的 enum,Rust 会用最节省空间的表示——通常是 1 字节或更少。

但真正的性能问题是:Rust 的 enum 需要额外的分支判断(match),而 Zig 的 tagged pointer 是零开销的——运行时只有一个值,不需要额外的分支。

// Zig: 零开销分支
pub fn run(task: *Task) void {
    const tag = task.getTag();
    switch (tag) {
        .Normal => runNormal(task.getPtr(NormalTask)),
        .Timeout => runTimeout(task.getPtr(TimeoutTask)),
        // ...
    }
}
// Rust: 有一个隐式的分支(但现代 CPU 的分支预测可以很好地处理)
fn run(task: &Task) {
    match task.kind {
        TaskKind::Normal => run_normal(task.data_ptr::<NormalTask>()),
        TaskKind::Timeout => run_timeout(task.data_ptr::<TimeoutTask>(), task.timeout_ms()),
        // ...
    }
}

好消息是,现代 CPU 的分支预测器对这种稀疏的 4 路分支预测准确率极高——实际性能差异在大多数场景下是可以忽略的。


五、Unsafe 大论战:68.1万行代码与13000个 unsafe

5.1 数字引爆社区

重写完成后,t3.gg 创始人 Theo 在 X 上发布了一组直接的数据对比:

项目Rust 代码量unsafe 调用数比例
uv (Astral)35万行73个1 : 4797
Bun Rust 版本68.1万行13000+ 个1 : 52

73 vs 13000——差了接近 180 倍。Rust 社区对 unsafe 的态度一向审慎:每个 unsafe 都是一处编译器无法保证内存安全的"信任边界"。13000 个 unsafe,几乎等于在说:"这段代码在 Rust 的安全网之外裸奔。"

5.2 Bun 的 unsafe 来源分类

深入分析 Bun 的 unsafe 来源,可以分为四类:

类型一:与 JavaScriptCore 的 FFI 交互(最大来源)

// JavaScriptCore 是用 C++ 编写的,所有交互都是 FFI
// 这是 Bun unsafe 的最主要来源,无法绕开

mod jsc {
    use std::os::raw::{c_char, c_int, c_void};
    
    extern "C" {
        // JSContextGroup creation
        pub fn JSContextGroupCreate() -> *mut jsc_context_group_t;
        pub fn JSContextGroupRelease(group: *mut jsc_context_group_t);
        
        // JSContext creation
        pub fn JSGlobalContextCreateInGroup(
            group: *mut jsc_context_group_t,
            global_object_class: *mut jsc_class_t,
        ) -> *mut jsc_context_t;
        
        // Script evaluation
        pub fn JSEvaluateScript(
            ctx: *mut jsc_context_t,
            script: *const c_char,
            this_obj: *mut jsc_object_t,
            source_url: *const c_char,
            start_line: c_int,
            exception: *mut *mut jsc_value_t,
        ) -> *mut jsc_value_t;

        // Object property access
        pub fn JSObjectGetProperty(
            ctx: *mut jsc_context_t,
            object: *mut jsc_object_t,
            property_name: *const c_char,
            exception: *mut *mut jsc_value_t,
        ) -> *mut jsc_value_t;
        
        // Array buffer management
        pub fn JSArrayBufferCreate(
            ctx: *mut jsc_context_t,
            bytes: *mut c_void,
            byte_length: usize,
            deallocator: Option<extern "C" fn(*mut c_void, *mut c_void)>,
            deallocator_context: *mut c_void,
        ) -> *mut jsc_value_t;
    }
}

// SAFETY: 
// - `ctx` must be a valid JSContext
// - `script` must be a valid null-terminated C string
// - `exception` must point to a valid pointer to Exception or NULL
unsafe fn eval_script(ctx: *mut jsc_context_t, script: &str) -> Result<*mut jsc_value_t> {
    let mut exception: *mut jsc_value_t = std::ptr::null_mut();
    let result = jsc::JSEvaluateScript(
        ctx,
        script.as_ptr() as *const c_char,
        std::ptr::null_mut(),
        std::ptr::null(),
        1,
        &mut exception,
    );
    if exception.is_null() {
        Ok(result)
    } else {
        Err(JSError::from_exception(ctx, exception))
    }
}

类型二:WebKit Malloc 分配器

// Bun 使用 WebKit 的内存分配器,而非 Rust 默认分配器
// 所有直接 malloc/free 调用都需要 unsafe

mod malloc_zone {
    use std::os::raw::c_void;
    use std::ptr::NonNull;
    
    extern "C" {
        pub fn malloc_zone_malloc(zone: *mut c_void, size: usize) -> *mut c_void;
        pub fn malloc_zone_free(zone: *mut c_void, ptr: *mut c_void);
        pub fn malloc_zone_realloc(zone: *mut c_void, ptr: *mut c_void, size: usize) -> *mut c_void;
        pub fn malloc_zone_batch_free(zone: *mut c_void, ptrs: *mut *mut c_void, count: usize);
    }
}

/// Bun 的自定义分配器,底层使用 WebKit Malloc
pub struct WebKitAlloc;

unsafe impl std::alloc::Allocator for WebKitAlloc {
    fn allocate(&self, layout: std::alloc::Layout) -> Result<std::ptr::NonNull<[u8]>, std::alloc::AllocError> {
        let zone = get_bun_malloc_zone(); // 获取 Bun 的 malloc zone
        let ptr = unsafe { malloc_zone::malloc_zone_malloc(zone, layout.size()) };
        if ptr.is_null() {
            return Err(std::alloc::AllocError);
        }
        Ok(std::ptr::NonNull::slice_from_raw_parts(
            std::ptr::NonNull::new_unchecked(ptr as *mut u8),
            layout.size(),
        ))
    }

    fn deallocate(&self, ptr: std::ptr::NonNull<u8>, _layout: std::alloc::Layout) {
        let zone = get_bun_malloc_zone();
        unsafe { malloc_zone::malloc_zone_free(zone, ptr.as_ptr() as *mut c_void) }
    }
    
    // ... 其他方法
}

类型三:POSIX 系统调用

// 文件描述符操作、网络 I/O —— 都是 unsafe 系统调用
use libc::{c_int, c_void, size_t, ssize_t};

// SAFETY:
// - `fd` must be a valid file descriptor
// - `buf` must point to a buffer of at least `count` bytes
// - the buffer must be valid for writes of `count` bytes
unsafe fn read_from_fd(fd: c_int, buf: &mut [u8]) -> Result<usize> {
    let ret = libc::read(fd, buf.as_mut_ptr() as *mut c_void, buf.len() as size_t);
    if ret < 0 {
        Err(std::io::Error::last_os_error())
    } else {
        Ok(ret as usize)
    }
}

unsafe fn write_to_fd(fd: c_int, buf: &[u8]) -> Result<usize> {
    let ret = libc::write(fd, buf.as_ptr() as *const c_void, buf.len() as size_t);
    if ret < 0 {
        Err(std::io::Error::last_os_error())
    } else {
        Ok(ret as usize)
    }
}

// epoll —— Linux 高性能 I/O 多路复用
#[repr(C)]
struct EpollEvent {
    events: u32,
    data: u64, // 存储文件描述符
}

unsafe fn create_epoll() -> Result<c_int> {
    let fd = libc::epoll_create1(0);
    if fd < 0 {
        Err(std::io::Error::last_os_error())
    } else {
        Ok(fd)
    }
}

类型四:SIMD Intrinsics(性能关键路径)

// 在字符串处理、base64 编码等性能关键路径使用 SIMD
// 这些操作必须通过 unsafe intrinsic 调用

#[cfg(target_arch = "x86_64")]
pub fn fast_base64_encode_simd(input: &[u8]) -> Vec<u8> {
    use std::arch::x86_64::*;
    
    let mut output = Vec::with_capacity((input.len() + 2) / 3 * 4);
    let mut i = 0;
    
    // Process 6 bytes at a time (48 bits -> 8 base64 chars)
    while i + 6 <= input.len() {
        // SAFETY: input has at least 6 bytes remaining
        let chunk = unsafe {
            _mm_loadu_si128(input.as_ptr().add(i) as *const __m128i)
        };
        
        // Encode 6 bytes -> 8 base64 chars using SIMD
        // (实际实现需要 lookup table + shift + mask)
        let encoded = encode_6bytes_simd(chunk);
        
        output.extend_from_slice(&encoded);
        i += 6;
    }
    
    // Handle remaining bytes (scalar fallback)
    // ...
    output
}

5.3 unsafe 治理策略

Phase B 的 unsafe 治理,有以下可行路径:

策略一:分层抽象——用 safe wrapper 包裹 unsafe 核心

// 不暴露 unsafe,只暴露安全的 API
pub struct File {
    fd: RawFd,
}

impl File {
    pub fn open(path: &Path) -> std::io::Result<Self> {
        let fd = unsafe { open_raw(path.as_bytes())? }; // unsafe 只在这里
        Ok(Self { fd })
    }
    
    pub fn read(&self, buf: &mut [u8]) -> std::io::Result<usize> {
        // 这里对用户是安全的
        unsafe { read_raw(self.fd, buf) }
    }
    
    pub fn write(&self, buf: &[u8]) -> std::io::Result<usize> {
        unsafe { write_raw(self.fd, buf) }
    }
    
    pub fn close(self) {
        let fd = self.fd;
        drop(self); // 自动调用 close
    }
}

策略二:类型化指针——让不变量在类型层面表达

// 用类型系统表达指针的生命周期约束
use std::marker::PhantomData;

// "指向已初始化内存的指针"
pub struct ValidPtr<T> {
    ptr: NonNull<T>,
    _marker: PhantomData<T>,
}

impl<T> ValidPtr<T> {
    pub fn new(ptr: NonNull<T>) -> Self {
        Self { ptr, _marker: PhantomData }
    }
    
    pub fn as_ref(&self) -> &T {
        // SAFETY: ValidPtr guarantees the pointer is valid
        unsafe { self.ptr.as_ref() }
    }
    
    pub fn as_mut(&mut self) -> &mut T {
        // SAFETY: &mut self guarantees exclusive access
        unsafe { self.ptr.as_mut() }
    }
}

// "指向已分配但未初始化内存的指针"
pub struct UninitPtr<T> {
    ptr: NonNull<T>,
    _marker: PhantomData<T>,
}

impl<T> UninitPtr<T> {
    pub fn write(self, value: T) -> ValidPtr<T> {
        // SAFETY: UninitPtr guarantees allocated memory, write completes initialization
        unsafe { self.ptr.as_ptr().write(value) };
        ValidPtr::new(self.ptr)
    }
}

六、测试策略:99.8%通过率背后的工程保障

6.1 为什么用既有测试而不是 AI 生成测试

Phase A 策略的核心洞察之一是:不要让 AI 生成测试用例,而是让 Rust 版本通过 Zig 版本积累的既有测试

这个决策背后有深刻的工程逻辑:

  1. 测试是质量的锚点:既有测试套件代表了"Bun 应该做什么"的规格说明书,是经过实际运行验证的
  2. AI 生成测试的覆盖偏差:AI 倾向于生成"能证明代码工作"的测试,而不是"能发现代码问题"的测试
  3. 回归检测的完整性:如果用 AI 生成新测试,很可能遗漏掉 Zig 版本中的边界情况

6.2 Bun 测试套件的结构

Bun 的测试套件包含多个层次:

第一层:单元测试

// bun:sql-string/src/sql-string.test.zig(示例)
test "basic string escaping" {
    const SqlString = @import("sql-string.zig");
    const str = SqlString.escape("O'Reilly");
    try testing.expect(eql(u8, str, "O\\'Reilly"));
}

test "unicode string escaping" {
    const SqlString = @import("sql-string.zig");
    const str = SqlString.escape("日本");
    try testing.expect(eql(u8, str, "日本")); // No escaping needed
}

第二层:集成测试

# bun test 运行的集成测试
$ bun test run packages/bun-tests/fetch/
✓ Fetch API basic (100 tests, 100 passed)
✓ Fetch API headers (50 tests, 49 passed, 1 skipped)
✓ Fetch API redirect (30 tests, 30 passed)
✓ Fetch API error handling (25 tests, 25 passed)

第三层:规范符合性测试

Bun 使用 Test262(ECMAScript 官方规范测试集)的子集来验证 JavaScript 引擎的符合性。

6.3 99.8% 的真实含义

"99.8% 测试通过" 并不意味着"100% 正确"。它意味着:

  • 测试覆盖之外的行为变化是未知的
  • 0.2% 的失败可能包含边缘情况,这些边缘情况可能在生产环境中被触发
  • 平台差异尚未解决(macOS/Windows)

关键的 0.2% 失败主要集中在:

  • Zig comptime 的编译期求值(Rust 没有等价机制)
  • macOS 特有的 Mach API 调用
  • 某些需要精确控制内存布局的底层测试

七、社区大讨论:AI 生成代码的质量与流程

7.1 Theo 的质疑:流程缺失

Theo 的数据对比引发了最激烈的讨论。他的核心论点是:

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

这个论断的后半部分——"由 Agents 审核并批准"——准确击中了问题的核心。

在传统的代码审查流程中:

Human 编写代码
    ↓
Human (reviewer) 审核代码
    ↓
Human (maintainer) 批准合并

在 Bun 的 Rust 重写中:

AI (Claude) 编写代码
    ↓
AI (Claude) 审核代码(Phase A 自审)
    ↓
Jarred (人类) 批准合并

唯一的"人类关卡"是最后的合并决策。但 Jarred 面对的是 4000 个 commits、96 万行代码——他不可能逐一审查,他能做的只有运行测试套件,看是否通过。

7.2 Jarred 的辩护:技术上的合理性

Jarred 的回应是务实的:

"当前状态仍然只是'勉强能动',绝对不能交付。下一步还要做大量代码清理,再让 Claude 继续啃测试套件。"

他的核心观点:

  1. Phase A 的目标只是"能编译、能通过测试",不是"能交付"
  2. Phase B 才是真正的质量提升阶段
  3. "我真的很厌倦为内存泄漏、崩溃和稳定性问题而担忧和花费大量时间进行修复"

最后一句话透露了真实的驱动力:不是技术浪漫主义,而是工程师的疲惫感。4700 个 open issues、反复的内存泄漏调试、与 Zig 上游的分歧——这些都是真实的工程负担。

7.3 Anthropic 收购的影响

关于"Anthropic 是否强迫 Bun 团队使用 Rust",Jarred 明确否认:

"没人逼我这么做。"

但值得注意的是,Anthropic 收购 Bun 后,Bun 的商业方向发生了明显变化:从"通用 JavaScript 运行时"转向"Claude Code 的专用基础设施"。这种战略优先级的变化,可能间接推动了"快速解决技术债务"的决策——与其在 Zig 生态中慢慢修复,不如用 AI 快速迁移到 Rust。


八、行业启示:AI 重写软件的边界与未来

8.1 Bun 证明了什么

速度前所未有:把一个 96 万行规模的系统从 Zig 迁移到 Rust,传统工程估算需要 3-6 个月(参考:Jarred 本人曾说,用手工花了三周迁移 esbuild 到 Rust)。Claude 用了 6 天,差了 10-20 倍。

规模前所未有:这不是局部模块的翻译,而是完整的、端到端的语言迁移——包括 JS 引擎集成、FFI 层、系统调用封装、测试套件全部迁移。

质量有底线:99.8% 测试通过率虽然不是 100%,但对于跨语言迁移来说已经极其高——证明了 Phase A "忠实翻译"策略的有效性。

8.2 Bun 暴露了什么

流程问题:没有人类审查的 AI 生成代码,在大语言模型(LLM)的推理能力范围内,质量是有边界的——而这个边界目前还不清楚在哪里。

unsafe 的隐形成本:13000 个 unsafe 不会因为 Rust 的类型系统而消失,它们只是被暂时"认可"了。这些 unsafe 就像是埋在代码库中的地雷,未来出现内存安全问题时会成为首要排查对象。

技术债务的跨语言传递:Zig 版本的内存泄漏、4700 个 open issues——这些问题并没有因为语言切换而消失。Rust 的所有权系统和借用检查器不能自动解决业务逻辑层面的内存泄漏。

8.3 更广泛的大趋势

Bun 不是孤例。类似的 AI 驱动极限重写正在多个领域同时发生:

项目迁移内容时间规模
CloudflareNext.js API → 自研实现1周中等
Ladybird 浏览器JavaScript 引擎 C++ → Rust2周大型
BunJavaScript 运行时 Zig → Rust6天超大型
VercelNode.js 服务批量重构数天多个服务

8.4 Jarred 的预言与现实的距离

Jarred 在 5月3日的推文中预言:

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

这个预言或许过于极端,但它指出了一个真实的方向:AI 辅助编程正在从"工具"进化为"执行者"

Bun 的这次重写证明了 AI 执行大规模代码生成的可能性,也暴露了流程保障(human-in-the-loop)的缺失。当 AI 可以以人类 10-20 倍的速度完成代码迁移时,我们需要重新思考:什么样的审查机制能够匹配这个速度?


九、未来展望:Rust 版 Bun 的 roadmap

9.1 短期目标(1-3个月)

Unsafe 治理

// 目标:将 unsafe 从 13000+ 降至 8000 以下
// 策略:识别可 safe 化的 unsafe 块

// 示例:之前
unsafe fn allocate_buffer(size: usize) -> *mut u8 {
    libc::malloc(size)
}

// 之后(safe wrapper)
fn allocate_buffer(size: usize) -> Result<NonNull<u8>, AllocError> {
    NonNull::new(unsafe { libc::malloc(size) })
        .ok_or(AllocError)
}

// 用 allocator pattern 封装所有 malloc/free
pub struct BunAllocator;
unsafe impl Allocator for BunAllocator { /* ... */ }

fn main() {
    let data: Vec<u8, BunAllocator> = Vec::with_capacity_in(1024, BunAllocator);
}

平台完善

macOS 和 Windows 平台的测试覆盖率提升,解决剩余的 0.2% 失败。

9.2 中期目标(3-6个月)

性能优化

Rust 版本相对于 Zig 版本的一个潜在优势:Rust 的编译器可以进行更激进的优化,而 Zig 的 comptime 优化能力相对有限。

// 利用 Rust 的 const generics 实现编译期优化
struct FixedBuffer<T, const N: usize> {
    data: [T; N],
    len: usize,
}

impl<T, const N: usize> FixedBuffer<T, N> {
    const fn new() -> Self {
        Self {
            data: unsafe { std::mem::zeroed() }, // 编译期已知大小
            len: 0,
        }
    }
}

// 栈上分配,无堆分配开销——编译器可以完全内联
let buffer: FixedBuffer<u8, 4096> = FixedBuffer::new();

async 生态集成

Phase B 可能引入 tokio 来替代 Zig 的单线程事件循环,这将是架构上最显著的变化之一——从 Zig 的协作式调度迁移到 Rust 的 async/await 生态。

9.3 长期目标(6-12个月)

  • 评估 Rust 的生命周期系统是否能够从根本上解决 Zig 版本的内存泄漏问题
  • 探索 WebAssembly 目标的可能性
  • 将经验沉淀为开源工具:zig-to-rust-migration-guide

十、总结:AI 重写软件的工程哲学反思

Bun 的六天重写,是 2026 年技术界最具标志性的事件之一。它证明了 AI 在大规模代码迁移中的可行性,也暴露了 AI 生成代码在质量保障流程上的根本缺陷。

从技术角度,我们可以得出以下结论:

AI 重写的优势是真实的

  • 速度:人类工程师无法企及的迁移速度(6天 vs 数月)
  • 规模:跨越语言壁垒的系统级迁移成为可能
  • 一致性:Phase A 模式下,行为保持高度一致

AI 重写的局限是真实的

  • 审查缺失:没有人类参与的质量保障是危险的
  • 隐性债务:技术债务不会因为语言切换而消失
  • unsafe 陷阱:快速迁移带来的大量 unsafe 调用,是未来维护的隐患

关键问题不是"AI 能否重写软件",而是"AI 重写的软件,如何建立人类信任"。

当你的 CTO 说"我们要把代码库从 X 语言重写成 Y 语言"时,他可能很快就会问:"Claude 需要几天?"而不是"需要几个月"。

而作为工程师,我们的任务是确保:速度上天的时代,信任不会成为牺牲品。


附录:关键时间线

日期事件
2022年Bun 1.0 发布,基于 Zig,目标为 Node.js 替代
2025年12月Anthropic 收购 Bun
2026年3月12日Claude Code Issue #33453 报告严重内存泄漏
2026年4月Bun v1.1.13 通过更换分配器降低 5% 内存占用
2026年5月初claude/phase-a-port 分支创建,Phase A 启动
2026年5月3日Jarred 透露想用 Rust 重写的实验
2026年5月7日~4000 commits,96万行,剩余3个编译错误
2026年5月9日Linux x64 glibc 测试通过率 99.8%
2026年5月11日Jarred 宣布合并 Rust 版本,"Zig 的最后一个版本"
2026年5月12日Theo 披露 unsafe 数量(13000+),社群大讨论
2026年6月Bun v1.3.14(Rust版)发布

本文基于 2026年6月公开信息撰写,分析角度为程序员视角的深度技术解读。


十一、深入对比:为什么 Bun 的 unsafe 比 uv 多两个数量级?

11.1 uv 的设计哲学:最小化 unsafe

Astral 的 uv 在设计之初就采取了"优先 safe Rust"的策略——所有与 Python C API 的交互,都通过精心的 safe wrapper 封装。

uv 的 unsafe 使用分布:

  • Python C API FFI:约 40 个( unavoidable)
  • SIMD Intrinsics:约 20 个
  • POSIX 系统调用:约 10 个
  • 总计:73 个

uv 之所以能保持这么低的 unsafe 数量,核心原因是:uv 是一个"纯 Rust 项目",底层只依赖 Python 的 C API。Python 的 C API 本身就是为 FFI 设计的,有相对清晰的 safe/unsafe 边界。

11.2 Bun 的 unsafe 是不可避免的结构性问题

Bun 的 unsafe 数量是结构性的,不可能降到 uv 的水平:

Bun 的底层依赖链:

Rust 应用代码
    ↓ unsafe FFI
Bun Runtime (Rust/Zig) ← 13000 个 unsafe 的主要来源
    ↓ C FFI  
JavaScriptCore Engine (C++) ← Safari/WebKit 的 JS 引擎,约 2000 万行 C++
    ↓ C 调用
WebKit Malloc (C) ← 自定义内存分配器
    ↓ 系统调用
Linux Kernel (C/SystemTap) / macOS Kernel (C/Objective-C) / Windows NT (C)

在这个依赖链中,JavaScriptCore 是最大的 unsafe 来源。Bun 使用 JavaScriptCore 而不是 V8,是因为 JavaScriptCore 的许可证(LGPL)更适合 Bun 的开源策略,但代价是与大量 C++ 代码的 FFI 交互。

11.3 unsafe 的可接受边界

从工程角度看,我们可以定义 unsafe 的"可接受边界":

// 好的 unsafe:FFI 边界清晰,有完整的 SAFETY 注释
mod ffi {
    extern "C" {
        /// Get the current system time in nanoseconds
        /// SAFETY: must be called from the main thread
        pub fn bun_hrtime_nanoseconds() -> u64;
    }
}

// 坏的 unsafe:语义模糊,注释缺失
unsafe {
    let x = *(ptr as *const u64);
}

// 好的 unsafe:明确的契约和类型
struct Fd(*mut libc::c_int); // 类型化文件描述符,强制安全使用

impl Fd {
    pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
        // 所有 unsafe 逻辑封装在 impl 内部,对外安全
        unsafe { sys_read(self.0, buf) }
    }
}

十二、AI 重写时代的工程最佳实践

12.1 从 Bun 学到的教训

Bun 的经历为 AI 辅助代码迁移提供了宝贵的经验教训:

教训一:Phase A/B 两阶段策略是正确的

Phase A 的"忠实翻译"确保了基础功能的完整性;Phase B 的"优化重构"则负责提升代码质量。这种分阶段策略,比"一边翻译一边优化"的混合策略更可控。

教训二:明确的编码规范比"让 AI 自己判断"更有效

Bun 的 PORTING.md 文档明确规定了:

  • 哪些 Rust crate 在 Phase A 中禁止使用
  • 哪些模式是鼓励的,哪些是禁止的
  • unsafe 必须附带的 SAFETY 注释格式

这种"明确规范"比"让 AI 自由发挥"的质量更稳定。

教训三:人类审查的缺失是最大的风险

Phase B 之后,Bun 应该建立一个专门的人类 code review 流程,对 Phase A 的代码进行逐步人工审查——即使只是随机抽样审查,也比完全没有审查好。

12.2 推荐的 AI 代码迁移流程

# 推荐的 AI 辅助代码迁移流程

阶段一:准备期(人类主导)
1. 编写详尽的 PORTING.md 规范文档
2. 建立基准测试套件(必须 100% 通过才能继续)
3. 确定迁移的优先级顺序(哪些模块先迁移,哪些后迁移)
4. 设定 unsafe 数量上限(作为质量红线)

阶段二:Phase A - 忠实翻译(AI 主导)
1. AI 按 PORTING.md 逐文件翻译
2. 每个文件必须通过编译和基准测试
3. 记录所有新增的 unsafe 和 TODO
4. 自动生成迁移报告

阶段三:Phase B - 质量提升(人机协作)
1. 人类 reviewer 随机抽样审查 Phase A 代码
2. AI 根据 reviewer 反馈进行重构
3. 逐步用 safe Rust 替换 unsafe 代码
4. 引入 Rust 惯用法和最佳实践

阶段四:验收与上线(人类主导)
1. 完整测试套件 100% 通过
2. 性能基准测试(必须不低于原版本)
3. 安全审计(重点审查 unsafe 块)
4. 逐步灰度发布,监控生产环境

12.3 unsafe 代码的安全审查清单

对于 AI 生成的 unsafe 代码,以下审查清单是必要的:

// unsafe 代码安全审查清单

// ✓ 有 SAFETY 注释?
// ✓ SAFETY 注释说明了不变量(invariants)?
// ✓ SAFETY 注释说明了调用者的义务(caller obligations)?
// ✓ 是否有对应的 safe wrapper?
// ✓ unsafe 块是否集中在 FFI 边界,而不是散落在业务逻辑中?
// ✓ 是否有 test case 验证 unsafe 代码的正确性?

// 示例:完整的 unsafe 审查注释
/// Reads `count` bytes from file descriptor `fd` into `buf`.
///
/// # SAFETY
/// - `fd` must be a valid, open file descriptor
/// - `buf` must be valid for writing `count` bytes
/// - `buf` must not overlap with any other borrow of `self`
/// - `fd` must not be in non-blocking mode (this function is blocking)
unsafe fn read_exact(&self, fd: RawFd, buf: &mut [u8], count: usize) -> io::Result<()> {
    let mut total_read = 0;
    while total_read < count {
        let n = unsafe { libc::read(fd, buf.as_mut_ptr().add(total_read), count - total_read) };
        match n {
            0 => return Err(io::Error::new(io::ErrorKind::UnexpectedEof, "unexpected EOF")),
            n if n > 0 => total_read += n as usize,
            n if n == -1 && io::Error::last_os_error().kind() == io::ErrorKind::Interrupted => continue,
            _ => return Err(io::Error::last_os_error()),
        }
    }
    Ok(())
}

十三、对 Rust 生态的影响

13.1 Bun 可能推动 Rust FFI 工具链的进化

Bun 的大规模 unsafe 使用,暴露了 Rust 生态在 FFI 工具链上的不足:

当前痛点:

  1. unsafe 的追踪困难:Rust 的类型系统无法追踪 unsafe 块的来源和目的
  2. SAFETY 注释的格式不统一:没有 clippy 或 rustc 内置工具来强制 SAFETY 注释格式
  3. unsafe 代码的测试覆盖要求不明确:safe 代码有 miri 来检测 UB,但 unsafe 代码的测试工具链相对薄弱

可能催生的工具:

  • unsafe-audit:自动扫描 unsafe 块,生成 SAFETY 覆盖率报告
  • cargo-fuzz-unsafe:专门针对 unsafe 代码的模糊测试工具
  • safe-wrapper-generator:从 unsafe FFI 自动生成 safe Rust wrapper
  • unsafe-tracker:追踪 unsafe 块的生命周期,从引入到消除

13.2 Bun 对 JavaScript 运行时格局的影响

Rust 版 Bun 的成功或失败,将直接影响 JavaScript 运行时的未来格局:

如果成功

  • 更多 Node.js 替代方案可能选择 Rust 作为实现语言
  • Deno 已经用 Rust,Bun 跟进后,Rust 可能成为"高性能 JS 运行时"的标准选择
  • JavaScriptCore 的地位可能提升(因为 Bun 使用 JSC 而不是 V8)

如果失败

  • 社区可能重新审视 AI 生成代码在基础设施项目中的可行性
  • "Rust 重写一切"的趋势可能暂时降温
  • 技术债务的跨语言传递问题将被更严肃地对待

13.3 人才的重新分配

一个有趣的观察:Zig 版本 Bun 的核心开发者中,有相当一部分是因为对 Zig 的热情而加入的。Rust 版 Bun 上线后,这些开发者会如何选择?

  • 继续参与 Rust 版本(技术更主流,unsafe 更多)
  • 回到 Zig 生态(继续开发纯 Zig 项目)
  • 分流到其他项目

这种人才的重新分配,可能成为 Zig 生态的一个转折点——如果大量 Bun 级别的开发者离开,Zig 的社区发展可能放缓。


十四、结语:技术债务、工程师疲惫与 AI 的双刃剑

14.1 为什么 Bun 选择了这条路

当我们回顾整个事件,有一个隐含的问题始终在背景中:为什么 Bun 没有选择在 Zig 生态内解决内存泄漏问题?

答案可能不是技术上的,而是工程上的:

4700 个 open issues 的背后,是工程师的疲惫感。当一个项目的技术债务积累到一定程度,继续在原有技术栈上修修补补的边际收益越来越低,而边际成本(理解复杂的历史代码、避免破坏性变更)却越来越高。

Rust 的所有权系统和生命周期检查,至少在理论上能够从根本上杜绝某些类型的内存安全问题。对于一个被内存泄漏困扰的项目来说,切换到一个"编译器能帮你检查内存安全"的语言,是一个理性的选择——即使切换本身也带来了大量 unsafe。

14.2 AI 的双刃剑

Bun 的故事也是 AI 辅助工程的双刃剑的缩影:

刀刃的一面:6 天完成 96 万行代码迁移,速度是传统工程的 10-20 倍——这是 AI 给工程师的赋能。

刀背的另一面:13000 个 unsafe,流程中的审查缺失,技术债务的跨语言传递——这是 AI 给工程师挖的坑。

关键问题是:工程师如何握好这把刀?

答案不在于"要不要用 AI",而在于"如何设计流程让 AI 的产出可控"。Phase A/B 策略、明确的 PORTING.md 规范、人类 review 的保留——这些都是"握刀"的技巧。

14.3 最后的思考

Jarred Sumner 的预言——"开源可能禁止人类贡献代码"——或许过于极端,但它指向了一个真实的趋势:AI 在代码生成上的能力边界正在快速扩展,而工程流程的进化速度,远没有跟上 AI 的能力扩展速度。

Bun 的六天重写,是这个时代的一个注脚。它不是一个完美的工程故事——13000 个 unsafe 就是证明。但它是一个真实的工程故事,充满了技术选择、哲学冲突、工程师疲惫和 AI 浪潮的交织。

对于我们这些身处其中的工程师来说,Bun 的故事给出了一个诚实的提醒:代码生成的速度可以上天,但信任的建立只能一步一步来。


附录一:Bun Rust 版本的模块结构

bun/
├── src/
│   ├── runtime/              # JavaScript 运行时核心
│   │   ├── js_runtime.rs     # 运行时入口
│   │   ├── js_context.rs     # JS 执行上下文
│   │   ├── gc.rs             # 垃圾回收(保留 JSC 的 GC)
│   │   └── mod.rs
│   │
│   ├── js/
│   │   ├── lib.rs            # JS 标准库绑定
│   │   ├── console.rs        # console API
│   │   ├── fetch.rs          # Fetch API
│   │   ├── buffer.rs         # Buffer API
│   │   └── process.rs        # process 对象
│   │
│   ├── http/
│   │   ├── client.rs         # HTTP 客户端(fetch)
│   │   └── server.rs         # HTTP 服务器
│   │
│   ├── bun/
│   │   ├── package_manager.rs # 包管理器
│   │   ├── bundler.rs        # 打包工具
│   │   ├── installer.rs      # npm install
│   │   └── test_runner.rs    # 测试运行器
│   │
│   ├── ffi/                  # FFI 边界层(unsafe 主要集中地)
│   │   ├── jsc_bindings.rs   # JavaScriptCore 绑定
│   │   ├── libc_bindings.rs  # POSIX 系统调用
│   │   ├── malloc_bindings.rs# WebKit Malloc 绑定
│   │   └── mod.rs
│   │
│   └── utils/
│       ├── buffer.rs         # 字节缓冲区
│       ├── string.rs         # 字符串处理
│       └── path.rs           # 路径处理
│
├── test/
│   ├── unit/
│   ├── integration/
│   └── spec/
│
└── Cargo.toml

附录二:关键参考链接

资源链接
Bun 官网https://bun.sh
Bun GitHubhttps://github.com/oven-sh/bun
Zig 官网https://ziglang.org
PORTING.md 文档Bun 仓库 claude/phase-a-port 分支
JavaScriptCore 文档https://developer.apple.com/documentation/javascriptcore
Rust FFI 规范 (NOMICON)https://doc.rust-lang.org/nomicon/

本文基于 2026年6月公开信息撰写。字数:约 8500 字。涵盖事件背景、技术架构、代码实战、性能分析、行业启示等多个维度。

复制全文 生成海报 Bun Rust Zig JavaScript AI编程 运行时 Claude Code

推荐文章

html一个包含iPhoneX和MacBook模拟器
2024-11-19 08:03:47 +0800 CST
解决 PHP 中的 HTTP 请求超时问题
2024-11-19 09:10:35 +0800 CST
WebSocket在消息推送中的应用代码
2024-11-18 21:46:05 +0800 CST
如何在Rust中使用UUID?
2024-11-19 06:10:59 +0800 CST
程序员茄子在线接单