Frontier · Bun Blog

用 Rust 重写 Bun

Jarred Sumner 解释了 Bun 的捆绑器从 Zig 重写为 Rust 的原因和方式,包括代理编码循环、对抗性审查、测试和迁移背后的性能工作。

本文由英文原文翻译。

文字版说明:原文中的数据图、交互可视化与流程示意未在本页复刻,完整图表请访问原文查看。

披露:Bun 于 2025 年 12 月被 Anthropic 收购。我和 Bun 团队的其他人在 <x​​10/> 工作。我使用了 Claude Fable 5 的预发行版本来进行 Rust 重写的大部分内容。

Bun 作为 esbuild 的 JavaScript 和 TypeScript 转译器的逐行端口启动,从 Go 到 Zig。我于 2021 年 4 月 16 日 写下了第一行 Zig。在看到 Hacker News 上的单页 Zig 语言参考 并对底层控制和对性能的关注感到非常兴奋后,我押注于 Zig。

从一开始,Bun 的范围就很大:

  • JavaScript、TypeScript 和 CSS 转译器、压缩器和捆绑器
  • npm 兼容的包管理器
  • Jest类似测试运行器
  • Node.js 和 TypeScript 兼容模块解析
  • HTTP/1.1 和 WebSocket 客户端
  • Node.js API 实现,例如 fsnettls 和许多其他模块

Bun 的初始版本是我在LLM之前的Zig时期,在奥克兰一间狭窄的公寓里用一年时间编写的。像 Bun 这样规模宏大的项目的默认结果是加入 GitHub 个人资料页面上的死角项目的墓地。 Zig 使 Bun 成为可能。如果没有Zig,我永远不可能在一年内建造这么多。

如今,Bun 的 CLI 每月下载量超过 2200 万次。 Claude Code 和 OpenCode 等流行工具将 Bun 作为它们的运行时。 Vercel、Railway、DigitalOcean 等为 Bun 提供第一方支持。

Bun 的范围也对稳定性构成了挑战。以下是我们在 Bun v1.3.14 中修复的一小部分错误:

  • 当异步 .write() 仍在线程池上进行时,在 zlib、Brotli 或 Zstd 流上调用 .reset() 时,node:zlib 中的堆使用后释放崩溃
  • onerror回调在本机句柄上发出可重入write(),然后发出close()时,node:zlib中的use-after-free崩溃
  • 当可重入的 JS 回调(例如超时侦听器、选项 getter 或写入回调内的 session.request())触发哈希图重新哈希时,node:http2 中的 use-after-free 崩溃,从而使内部流指针无效
  • UDPSocket.send()sendMany() 中的 use-after-free,其中 valueOf()toString() 回调中的用户代码可能会在负载捕获和实际发送之间分离 ArrayBuffer
  • valueOf 回调在参数强制期间分离或调整底层 ArrayBuffer 的大小时,Buffer#copyBuffer#fill 中的读取会发生崩溃和越界
  • 当套接字的连接状态通过用户 JS 回调在迭代中更改时,UDPSocket.sendMany() 中的堆越界写入
  • crypto.scrypt 中存在内存泄漏,当输出缓冲区分配失败时,回调和受保护的密码/盐缓冲区从未被释放
  • SSLWrapper.init 在错误路径上泄露了 strdup 的密码
  • tlsSocket.setSession() 中的内存泄漏,由于 d2i_SSL_SESSION 之后缺少 SSL_SESSION_free,每次调用都会泄漏一个 SSL_SESSION(每个调用约 6.5 KB)
  • 内存泄漏,其中fs.watch()观察者在.close()之后从未被垃圾回收,这是由于引用计数下溢将每个观察者永久固定为GC根
  • background-clip 具有供应商前缀和多层背景时,CSS 解析器会发生双重释放崩溃
  • DuplexUpgradeContext 从未被释放 - 每个 tls.connect({ socket: duplex }) 完全泄漏
  • MessageEvent 中的竞争条件崩溃,其中 GC 标记线程在从 BroadcastChannelMessagePort 并发访问期间可能会观察到 m_data 中的撕裂变体

我们本可以永久地一次性修复此类错误,但我们应该感谢我们的用户,他们希望我们做得更好,并系统地防止此类错误再次发生。

我们已经在做什么

  • 我们修补了 Zig 编译器以添加 Address Sanitizer 支持。我们在每次提交时都使用 ASAN 运行测试套件。
  • 我们在 Windows 上发布 Zig 经过安全检查的 ReleaseSafe 版本
  • 我们使用 Fuzzilli 对 Bun 的运行时 API 进行 24/7 模糊测试,V8 和 JavaScriptCore 使用的 JavaScript 引擎模糊器
  • 我们进行了大量的端到端内存泄漏测试

这比许多项目都多。

只要非常聪明,不要犯错误?

我们的错误修复列表感觉很糟糕,我厌倦了睡觉时担心Bun中的崩溃。我不会为此责怪 Zig - Zig 的其他用户没有我们所遇到的错误,并且将 GC 与手动管理内存混合对于软件来说是一件不常见的事情,没有语言真正为它设计。如果没有 Zig,我们就不会走到这一步,我将永远感激不已。直到最近,对于像 Bun 这样的项目,编程语言的选择还是一种单向决策。

JavaScript 是一种垃圾收集语言,现代 JavaScript 引擎(例如 JavaScriptCore)(和 V8)对于异常处理和垃圾收集器有严格的规则。 Zig 与 C 一样,不为您管理内存,这是一种权衡,对于许多项目来说,这是使用 Zig 的一个重要原因。 Zig 没有构造函数/析构函数,并且大多数清理工作预计在每个调用站点使用 defer 显式编写。

对于 Bun 来说,正确处理垃圾收集值和手动管理值的生命周期一直是稳定性问题的主要根源 - 最常见的是小内存泄漏,偶尔还会发生崩溃。每个内存分配都必须仔细检查。这些字节在哪里被释放?我们如何确保它只被释放一次?我们是否正确检查了 JavaScript 异常?这个垃圾收集指针对保守的堆栈扫描器可见吗?这是垃圾回收的内存还是手动管理的内存?

对于稳定性问题,尽早了解是最好的。模糊测试发生在代码合并之后。 CI 在推送代码时发生。运行时安全检查和地址清理程序在代码运行时发生(希望在开发中,在 CI 之前)。

减少此类问题的一种常见方法是确保清理代码始终为需要它的代码运行一次。 Zig 被设计为一种没有隐藏控制流的简单语言,因此它更喜欢使用显式 defer 关键字在作用域末尾运行代码,而不是 C++ 的隐式 ~Destructor 或 Rust 的隐式 Drop

语言清理
Zigdefererrdefer
C++~析构函数,&&移动
Rust放下

对于Zig代码,我们到底应该在什么时候运行清理代码?如果我们将相同的 *T 传递给许多不同的函数,我们如何知道它何时不再可访问并且可以被清理?当有些函数调用完后需要继续引用内存时是如何工作的?我们当前的方法是以下方法的组合:

  • arena 生命周期,可访问的范围是明确的(解析器状态不会转义调用函数,因此 AST 节点是一个不错的选择)
  • 引用计数
  • 密切关注

许多项目选择通过风格指南来回答此类问题。 TigerBeetle 的 TigerStyle 是 Zig 中的一个示例,Google 的 31,000 字的 C++ 样式指南 是另一个示例。风格指南的挑战在于执行。您如何确保遵循风格指南?从历史上看,代码审查是通过 linter 和静态分析器尽力执行的答案。

对于 Bun 来说,拥有一个严格的风格指南,并在类型系统中明确阐明明确的所有权期望,是一个真正的选择。由于 Zig 没有运算符重载,因此我们可能会得到很多如下所示的代码:

fn foo(a_ptr: SharedPtr(TCPSocket)) !void {
  const a: *TCPSocket = a_ptr.get();
  defer a_ptr.deref();

  const b = try do_something_with_a(a);
  defer b.deref();

  // ...
}

这比我们预期的 Zig 不太符合人体工程学:

fn foo(a: *TCPSocket) !void {
  const b = try do_something_with_a(a);
  // ...
}

C/C++ 呢?

大约 20% 的 Bun 代码是用 C++ 编写的,并且 Bun 嵌入了多个 C/C++ 库:

  • JavaScriptCore,为 Safari 提供支持的 JavaScript 引擎
  • uWebSockets 和 usockets - 我们的 HTTP/WebSocket 服务器和事件循环
  • lshpack 和 lsquic - HPACK 和 HTTP/3 库
  • BoringSSL,Google 的 OpenSSL 分支
  • SQLite

C++ 而不是 Zig 是一个合理的选择。我们会得到构造函数和析构函数。我们可以删除大量 extern "C" 包装器代码。

但是,我们仍然依赖通过代码审查强制执行的样式指南,即使使用 ASAN,内存损坏和内存泄漏仍然会发生。

为什么Rust?

该列表中的大部分错误都是释放后使用、双重释放以及错误路径中的“忘记释放”。在安全的 Rust 中,这些是编译器错误和使用 Drop 进行类似 RAII 的自动清理。编译器错误是比样式指南更好的反馈循环。

从历史上看,重写是一个糟糕的主意。不包括注释,Bun 是 Zig 的 535,496 行。用另一种语言重写需要一小群工程师花费一整年的时间。这意味着冻结错误修复、安全修复或功能开发。获得可交付产品的风险最小的方法是从 Zig 到 Rust 的机械端口,以最少的行为变化,使用我们已经用于测试 Bun 的完全相同的测试套件。

幸运的是,Bun自己的测试套件是用TypeScript编写的,这意味着它不依赖于运行时的编程语言。

一年对用户的影响为零并不是我们可以考虑的现实选择。因此,通过代码风格强制执行来解决稳定性问题是我们最好的选择,也是我们在将 Rust 启发的智能指针 添加到 Bun 的代码库时的计划。

但说实话,我不想这么做。国产智能指针的人体工程学效果比 Rust 更差,而且没有任何保证。

如果我花一周时间测试 Anthropic 的新模型是否可以在 Rust 中重写 Bun 呢?

一开始,我没想到它会起作用。几天后,测试套件的大部分开始通过,我看到新的 Rust 代码与原始 Zig 代码库的匹配程度。我的意见从“这值得尝试”变成“我要合并这个”。

Claude,将Bun重写为Rust。

有很多方法可以把这件事做得很糟糕。例如,提示Claude“用Rust重写Bun。不要犯任何错误。”然后祈祷它会起作用并不是我所做的。

想想一个人会如何做到这一点。第一个大问题是:

增量重写?或者,一次性完成所有事情?

根据我将 esbuild 的转译器从 Go 移植到 Zig 的 Bun 初始版本(没有 LLM)的经验,一切都更好。增量重写会添加临时代码,您希望这些代码最终会被删除,并且在中短期内会很痛苦。

第二个大问题:如何?

我们如何使 Rust 中的 Bun 与以前的 Bun 保持相同,具有相同的架构、性能和功能集,同时还获得 Rust 的语言功能(如借用检查器)?如何保证重写后团队仍能维护?

进行重写,看起来就像我们将 Zig 代码转译为 Rust。我们可以逐渐重构它以减少 unsafe 的使用,并在 Bun v1.4 发布后看起来更像惯用的 Rust。

这是仅有的两个大问题。其他一切都是策略。

编写和审查代码的循环

软件工程师的许多日常工程工作可能会被过度简化为循环。

// Pseudocode, not real code:
let task;
while ((task = todoList.pop())) {
  const result = task();
  const feedback = await Promise.all([review(result), review(result)]);
  await apply(feedback, result);
}

task 有一些与之关联的上下文(Jira 票证、GitHub 问题等)。 result 是您为修复它而编写的代码。代码审查者review检查回归和正确性的更改。然后你处理反馈。

我在 Rust 中重写了 Bun,使用 Claude Code 中的约 50 个动态工作流程在 11 天的时间内连续运行。

每个动态工作流程都是这样的循环 - 工作流程:

  • 生成将 Zig 模式和类型映射到 Rust 模式和类型的移植指南
  • 将每个 .zig 文件机械地移植到 .rs 文件,匹配 PORTING.md 和 LIFETIMES.tsv
  • 修复每个 crate 的编译器错误
  • bun testbun build 等子命令发挥作用
  • 让Bun整个测试套件中的每个测试都通过
  • 几个大型重构和清理过程

在这 11 天(及之后)的大部分时间里,我监控工作流程 - 手动读取输出以检查问题和错误,并提示 Claude 编辑循环以修复问题。

您如何审核添加了 100 万行以上的 PR?您如何开始建立负责任地合并大量 LLM 编写的代码所需的信心?

独立于语言的测试套件,具有一百万个断言、对抗性代码审查,当出现问题时,修复生成代码的过程,而不是手动修复代码。

对抗性审查

对抗性审查要求 Claude(在单独的上下文窗口中)详尽地提出更改会产生错误或不起作用的原因。

分割上下文窗口

通常对于人类来说,审查代码的人不是编写代码的人。编写代码的人想要合并代码,这可能会导致他们的操作在代码准备好之前就发布。

Claude也是一样。编写代码的Claude希望代码能够被接受。进行审查的Claude想要找到代码中的问题。

1 个实施者,每个实施者有 2 个或更多对抗性审核者。审阅者唯一的工作:查找错误以及代码不起作用的原因。实施者不审查。审核者未实施。

bug 1 of 3 · 异步关闭

它的背景:.zig 原始版本、移植计划、它自己的推理

其上下文:仅差异。被告知假设代码是错误的。

src/runtime/api/bun/js_bun_spawn_bindings.rs · 编译干净

对于 [spawned_stdout, spawned_stderr] 中的 stdio {

匹配标准输入输出{

StdioResult::Buffer(mut 管道) => {

// pipeline: Boxuv::Pipe — 将其交给 libuv 来关闭

pipe.close(Subprocess::on_pipe_close)

}

StdioResult::Fd(fd) => fd.close(),

StdioResult::不可用 => {}

}

}

uv_close 是异步的:libuv 保留原始句柄指针直到下一个循环标记,然后调用 on_pipe_close,这会释放分配。但是 `pipe` 是一个在匹配臂结束时掉落的 Box — libuv 保留已释放的内存,然后 close 回调会第二次释放它。释放后使用,然后双重释放。

Box::leak(pipe).close(Subprocess::on_pipe_close)

f0a454376c7 · win-review: js_bun_spawn_bindings.rs 在异步 uv_close 之前泄漏 Boxuv::Pipe 以避免 on_pipe_close 中的 UAF/双重释放

敌对审阅者实际上发现了三个错误 - 每个引用的提交都在主题行中带有其审阅属性。三者均已编译;这三个看起来似乎都有道理。审阅者是其自己的上下文窗口中的第二个Claude:它只获得差异,没有其他任何东西 - 没有实现者的推理 - 并被告知找出错误的方式。代码是从引用的提交中浓缩而来的;相同的错误,相同的修复。

这看起来像什么?

如果您要做一些大型且昂贵的事情,首先降低风险可以节省时间和金钱。

准备工作

在编写任何代码之前,我花了大约 3 个小时与 Claude 讨论如何将 Zig 代码库中的模式紧密映射到 Rust。 Claude 将这次讨论序列化为 PORTING.md 文档,最终发布在 Hacker News 上。

下一个问题:如何将 Rust 生命周期添加到手动管理内存的代码中?

这就是我向 Claude 发出这样的提示:

我:让我们开始一个动态工作流程来分析代码库中每个结构体字段的正确生命周期。此工作流程应读取每个文件中的每个结构字段并跟踪控制流。首先,在 Rust 中查找 express 具有复杂生命周期的结构体字段,然后为该字段提出一个生命周期,然后使用 2 个对抗性审查代理来审查该生命周期,然后应用任何反馈并序列化到 LIFETIMES.tsv 中以供其他Claude查看。

然后对 PORTING.mdLIFETIMES.tsv 进行一轮对抗性审查,以解决任何冲突的建议并仔细检查所有内容。我也手动读了一下。

试运行

在要求 Claude 将所有 1,448 个 .zig 文件转换为 .rs 文件之前,我只从 3 个文件开始。对于这 3 个文件中的每一个,1 个实施者编写了新的 .rs 文件,2 个敌对审阅者检查 .rs 文件与 .zig 文件的行为是否匹配,并且它遵循 PORTING.mdLIFETIMES.tsv。此后,1 名修复者采纳了所有建议。

错误开始

我要求 Claude 在所有 1,448 个 .zig 文件上循环工作流程,大约 2 分钟后,一个 Claude 在提交之前运行了 git stash。另一个运行git stash pop。然后git reset HEAD --hard。他们互相踩着对方!如果我将每个 Claude 放入单独的工作树中,我将耗尽磁盘空间,因为 Bun 的 git 存储库太大,最终需要将更改一起编译和查看。

因此,我要求 Claude 编辑工作流程,以指示 Claude 永远不要运行 git stashgit reset 或任何不立即提交特定文件的 git 命令。也没有cargo。根本没有慢命令。

然后,Claude恢复了工作流程。它正在发挥作用!太慢了,所以我将其分成 4 个工作流分片,每个分片都有自己的工作树(总共 4 个工作树),每个分片运行 16 个Claude提交和推送文件。

终于写代码了

得益于所有并行化和这些准备工作,Claude 在高峰期每分钟编写了大约 1,300 行代码。每行代码都由两个独立的敌对审阅者(也是Claude)进行审阅,并在提交之前经过一轮修复。绝对没有任何效果。

11天×24小时·PDT

5,463 次提交

1695 次提交/小时

12am6am12pm6pm5 月 4 日,太平洋夏令时上午 7 点至上午 8 点 — 6 次提交,+89,278 行 5 月 4 日,太平洋夏令时上午 8 点至上午 9 点 — 2 次提交,+50,742 行 5 月 4 日,太平洋夏令时上午 9 点至上午 10 点 — 1 次提交,+28,149 行 5 月 4 日,太平洋夏令时间上午 11 点至中午 12 点 — 1 次提交, +39,752 行 5 月 4 日中午 12 点至下午 1 点 PDT — 3 次提交,+251,616 行 5 月 4 日下午 1 点至下午 2 点 PDT — 2 次提交,+161,724 行 5 月 4 日下午 3 点至下午 4 点 PDT — 3 次提交,+136,381 行 5 月 4 日下午 5 点至下午 6 点 PDT — 5 次提交,+895 行 5 月 4 日,下午 6 点至下午 7 点(太平洋夏令时) — 5 次提交,+17,027 行 5 月 4 日,下午 7 点至晚上 8 点(太平洋夏令时)— 1 次提交,+106 行 5 月 4 日,晚上 9 点至晚上 10 点(太平洋夏令时)— 13 次提交,+11,661 行 5 月 4 日,晚上 11 点至上午 12 点(太平洋夏令时)— 6 次提交,+8,516 行 5 月 5 日,太平洋夏令时 12 点至凌晨 1 点 — 9提交,+1,381 行 5 月 5 日,太平洋夏令时间上午 1 点至凌晨 2 点 — 7 次提交,+1,577 行 5 月 5 日,上午 2 点至凌晨 3 点(太平洋夏令时间) — 4 次提交,+2,035 行 5 月 5 日,太平洋夏令时间上午 3 点至凌晨 4 点 — 4 次提交,+7,808 行 5 月 5 日,太平洋夏令时间上午 4 点至上午 5 点 — 1 次提交,+2,796 行 5 月 5 日,上午 5 点至上午 6 点(太平洋夏令时间) — 2 次提交,+29,370 行 5 月 5 日,上午 8 点至上午 9 点(太平洋夏令时) — 2 次提交,+7,076 行 5 月 5 日,上午 9 点至上午 10 点(太平洋夏令时) — 2 次提交,+308 行 5 月 5 日,上午 11 点至中午 12 点(太平洋夏令时) — 2 次提交,+1,643 行 5 月 5 日,中午 12 点至下午 1 点(太平洋夏令时间) — 4 次提交, +1,452 行 5 月 5 日,下午 1 点至下午 2 点(太平洋夏令时) — 1 次提交,+2,142 行 5 月 5 日,下午 2 点至下午 3 点(太平洋夏令时间) — 4 次提交,+7,787 行 5 月 5 日,下午 3 点至下午 4 点(太平洋夏令时间) — 2 次提交,+5,835 行 5 月 5 日,下午 4 点至下午 5 点(太平洋夏令时间) — 1 次提交,+3,417 行 5 月 5 日,下午 5 点至下午 6 点(太平洋夏令时间)— 4 次提交,+3,960 行 5 月 5 日,下午 6 点至晚上 7 点(太平洋夏令时) — 4 次提交,+9,179 行 5 月 5 日,晚上 7 点至晚上 8 点(太平洋夏令时间) — 4 次提交,+1,983 行 5 月 5 日,晚上 8 点至晚上 9 点(太平洋夏令时) — 4 次提交,+18,902 行 5 月 5 日,晚上 9 点至晚上 10 点(太平洋夏令时间) — 43 次提交,+40,650 行太平洋夏令时间 5 日晚上 10 点至晚上 11 点 — 139 次提交,+64,842 行 5 月 5 日,下午 11 点至凌晨 12 点(太平洋夏令时) — 141 次提交,+34,814 行 5 月 6 日,太平洋夏令时间上午 12 点至凌晨 1 点 — 60 次提交,+10,417 行 5 月 6 日,太平洋夏令时间上午 1 点至凌晨 2 点 — 296 次提交,+38,530 行5 月 6 日,凌晨 2 点至 3 点(太平洋夏令时间) — 306 次提交,+18,836 行 5 月 6 日,凌晨 3 点至凌晨 4 点(太平洋夏令时间) — 196 次提交,+10,245 行 5 月 6 日,上午 4 点至上午 5 点(太平洋夏令时间) — 86 次提交,+2,655 行 5 月 6 日,上午 5 点至上午 6 点(太平洋夏令时间) — 16 次提交,+289 行 5 月 6 日,上午 8 点至上午 9 点PDT — 5 次提交,+264 行 5 月 6 日,上午 9 点至上午 10 点 PDT — 458 次提交,+16,409 行 5 月 6 日,上午 10 点至上午 11 点 PDT — 695 次提交,+44,000 行 5 月 6 日,上午 11 点至中午 12 点 PDT — 102 次提交,+21,972 行 5 月 6 日,中午 12 点至下午 1 点 PDT — 19 次提交,+2,891 行 5 月 6 日,下午 1 点至下午 2 点(太平洋夏令时) — 3 次提交,+56 行 5 月 6 日,下午 3 点至下午 4 点(太平洋夏令时间) — 64 次提交,+3,606 行 5 月 6 日,下午 4 点至下午 5 点(太平洋夏令时间) — 264 次提交,+60,132 行 5 月 6 日,下午 5 点至下午 6 点(太平洋夏令时)— 268 次提交,+40,953 行6、下午 6 点至晚上 7 点(太平洋夏令时) — 281 次提交,+16,283 行 5 月 6 日,晚上 7 点至晚上 8 点(太平洋夏令时间) — 258 次提交,+26,654 行 5 月 6 日,晚上 8 点至晚上 9 点(太平洋夏令时间) — 327 次提交,+16,599 行 5 月 6 日,晚上 9 点至晚上 10 点(太平洋夏令时) — 74 次提交,+8,331 行 5 月 6 日,太平洋夏令时晚上 10 点至晚上 11 点 — 17 次提交,+2,200 行 5 月 6 日,晚上 11 点至上午 12 点 PDT — 11 次提交,+3,590 行 5 月 7 日,上午 12 点至凌晨 1 点(太平洋夏令时间) — 17 次提交,+6,577 行 5 月 7 日,上午 1 点至凌晨 2 点 PDT — 22 次提交,+8,718 行 5 月 7 日,凌晨 2 点至凌晨 3 点PDT — 21 次提交,+11,392 行 5 月 7 日,上午 3 点至凌晨 4 点 PDT — 53 次提交,+6,476 行 5 月 7 日,上午 4 点至上午 5 点 PDT — 31 次提交,+2,356 行 5 月 7 日,上午 5 点至上午 6 点 PDT — 9 次提交,+1,787 行 5 月 7 日,上午 6 点至上午 7 点 PDT — 4 次提交,+580 行7 日,上午 7 点至上午 8 点 PDT — 5 次提交,+181 行 5 月 7 日,上午 11 点至下午 12 点 PDT — 3 次提交,+421 行 5 月 7 日,中午 12 点至下午 1 点 PDT — 1 次提交,+13 行 5 月 7 日,下午 1 点至下午 2 点 PDT — 5 次提交,+248 行 5 月 7 日,下午 2 点至下午 3 点 PDT — 9 次提交,+2,131 行太平洋夏令时间 7 日下午 3 点至下午 4 点 — 51 次提交,+3,207 行 5 月 7 日下午 4 点至下午 5 点(太平洋夏令时) — 56 次提交,+2,647 行 5 月 7 日,下午 5 点至下午 6 点(太平洋夏令时间) — 159 次提交,+2,787 行 5 月 7 日,下午 6 点至下午 7 点(太平洋夏令时间) — 42 次提交,+1,590 行 5 月 7 日,晚上 7 点至晚上 8 点(太平洋夏令时间)— 46 次提交,+4,170 行 5 月 7 日,晚上 8 点至晚上 9 点(太平洋夏令时间) — 52 次提交,+2,113 行 5 月 7 日,晚上 9 点至晚上 10 点(太平洋夏令时间) — 27 次提交,+1,585 行 5 月 7 日,晚上 10 点至晚上 11 点(太平洋夏令时间) — 27 次提交,+2,231 行 5 月 7 日,晚上 11 点至上午 12 点(太平洋夏令时) — 30 次提交, +4,987 行 5 月 8 日,太平洋夏令时间上午 12 点至凌晨 1 点 — 27 条提交,+1,196 行 5 月 8 日,凌晨 1 点至凌晨 2 点(太平洋夏令时间) — 14 条提交,+904 行 5 月 8 日,上午 2 点至凌晨 3 点(太平洋夏令时) — 8 条提交,+536 行 5 月 8 日,上午 3 点至凌晨 4 点(太平洋夏令时间) — 13 条提交,+253 行 5 月 8 日,凌晨 4 点至凌晨 5 点PDT — 3 次提交,+771 行 5 月 8 日上午 5 点至上午 6 点 PDT — 15 次提交,+1,545 行 5 月 8 日,上午 6 点至上午 7 点 PDT — 12 次提交,+1,965 行 5 月 8 日,上午 7 点至上午 8 点 PDT — 14 次提交,+1,866 行 5 月 8 日,上午 8 点至上午 9 点 PDT — 55 次提交,+3,622 行8 日上午 9 点至上午 10 点 PDT — 35 次提交,+4,778 行 5 月 8 日,上午 10 点至上午 11 点 PDT — 1 次提交,+0 行 5 月 8 日,中午 12 点至下午 1 点 PDT — 1 次提交,+116 行 5 月 8 日下午 1 点至下午 2 点 PDT — 2 次提交,+66 行 5 月 8 日,下午 2 点至下午 3 点 PDT — 9 次提交,+1,071 5 月 8 日,下午 3 点至 4 点(太平洋夏令时) — 26 条提交,+1,691 行 5 月 8 日,下午 4 点至下午 5 点(太平洋夏令时间) — 18 条提交,+2,751 行 5 月 8 日,下午 5 点至下午 6 点(太平洋夏令时) — 2 条提交,+97 行 5 月 8 日,下午 6 点至下午 7 点(太平洋夏令时) — 2 条提交,+135 行 5 月 8 日,下午 7 点至晚上 8 点(太平洋夏令时间) — 11 条提交, 5 月 8 日晚上 8 点至晚上 9 点(太平洋夏令时)+1,763 行 — 20 次提交,+5,272 行 5 月 8 日晚上 9 点至晚上 10 点(太平洋夏令时)— 12 次提交,+952 行 5 月 8 日晚上 10 点至晚上 11 点(太平洋夏令时)— 2 次提交,+334 行 5 月 8 日晚上 11 点至上午 12 点(太平洋夏令时)— 6 次提交,+2,033 行 5 月 9 日,5 月 9 日,上午 12 点至凌晨 1 点 PDT — 9 次提交,+387 行 5 月 9 日,上午 1 点至凌晨 2 点 PDT — 9 次提交,+723 行 5 月 9 日,凌晨 2 点至凌晨 3 点 PDT — 8 次提交,+98 行 5 月 9 日,上午 3 点至凌晨 4 点 PDT — 63 次提交,+2,538 行 5 月 9 日,上午 4 点至上午 5 点 PDT — 11 次提交,+8,861 行9 日上午 5 点至上午 6 点 PDT — 4 次提交,+42 行 5 月 9 日上午 6 点至上午 7 点 PDT — 3 次提交,+2,616 行 5 月 9 日,上午 7 点至上午 8 点 PDT — 6 次提交,+6,993 行 5 月 9 日,上午 8 点至上午 9 点 PDT — 1 次提交,+3,705 行 5 月 9 日,上午 9 点至上午 10 点 PDT — 11 次提交, +199 行 5 月 9 日上午 11 点至中午 12 点 PDT — 1 次提交,+23 行 5 月 9 日下午 12 点至下午 1 点 PDT — 4 次提交,+5,012 行 5 月 9 日,下午 1 点至下午 2 点 PDT — 7 次提交,+2,080 行 5 月 9 日下午 2 点至下午 3 点 PDT — 6 次提交,+924 行 5 月 9 日下午 3 点至下午 4 点 PDT — 5提交,+248 行 5 月 9 日,下午 4 点至下午 5 点(太平洋夏令时) — 17 次提交,+508 行 5 月 9 日,下午 5 点至下午 6 点(太平洋夏令时间) — 2 次提交,+135 行 5 月 9 日,下午 6 点至下午 7 点(太平洋夏令时) — 4 次提交,+822 行 5 月 9 日,下午 7 点至晚上 8 点(太平洋夏令时) — 1 次提交,+7 行 5 月 10 日,5 月 10 日,上午 12 点至凌晨 1 点(太平洋夏令时间)— 4 次提交,+497 行 5 月 10 日,太平洋夏令时上午 1 点至凌晨 2 点 — 2 次提交,+35 行 5 月 10 日,太平洋夏令时间上午 2 点至凌晨 3 点 — 1 次提交,+131 行 5 月 10 日,太平洋夏令时间上午 3 点至凌晨 4 点 — 2 次提交,+322 行 5 月 10 日,太平洋夏令时间上午 4 点至上午 5 点 — 1 次提交,+3 行 5 月 10 日,太平洋夏令时间上午 5 点至上午 6 点 — 1提交,5 月 10 日上午 6 点至上午 7 点(太平洋夏令时)+26 行 — 2 次提交,5 月 10 日上午 7 点至上午 8 点(太平洋夏令时)+81 行 — 1 次提交,5 月 10 日上午 8 点至上午 9 点(太平洋夏令时间)+5 行 — 4 次提交,+78 行 5 月 10 日,太平洋夏令时上午 9 点至上午 10 点 — 1 次提交,+1 行 5 月 10 日太平洋夏令时间上午 10 点至上午 11 点 — 2 次提交, +128 行 5 月 10 日,上午 11 点至中午 12 点 PDT — 1 次提交,+4 行 5 月 10 日,中午 12 点至下午 1 点 PDT — 2 次提交,+413 行 5 月 10 日,下午 1 点至下午 2 点 PDT — 1 次提交,+25 行 5 月 10 日,下午 2 点至下午 3 点 PDT — 5 次提交,+327 行 5 月 10 日,下午 3 点至下午 4 点 PDT — 6 次提交, +1,172 行 5 月 10 日下午 4 点至 5 点 PDT — 4 次提交,+752 行 5 月 10 日下午 5 点至下午 6 点 PDT — 3 次提交,+227 行 5 月 10 日下午 6 点至 7 点 PDT — 2 次提交,+242 行 5 月 10 日下午 7 点至晚上 8 点 PDT — 1 次提交,+306 行 5 月 10 日下午 8 点至晚上 9 点 PDT — 1提交,+54 行 5 月 10 日,晚上 9 点至晚上 10 点 PDT — 2 次提交,+75 行 5 月 10 日,晚上 10 点至晚上 11 点 PDT — 1 次提交,+134 行 5 月 10 日,晚上 11 点至上午 12 点 PDT — 5 次提交,+103 行 5 月 11 日,12 点至凌晨 1 点 PDT — 2 次提交,+150 行 5 月 11 日,凌晨 1 点至凌晨 2 点 PDT — 4 次提交,+398 行 5 月 11 日,上午 2 点至凌晨 3 点 PDT — 2 次提交,+364 行 5 月 11 日,上午 3 点至凌晨 4 点 PDT — 3 次提交,+44 行 5 月 11 日,上午 4 点至上午 5 点 PDT — 7 次提交,+9,367 行 5 月 11 日,上午 6 点至上午 7 点 PDT — 2 次提交,+43 行太平洋夏令时 11 日上午 7 点至上午 8 点 — 2 次提交,+149 行 5 月 11 日上午 8 点至上午 9 点(太平洋夏令时间) — 10 次提交,+2,171 行 5 月 11 日,太平洋夏令时上午 9 点至上午 10 点 — 16 次提交,+2,047 行 5 月 11 日,太平洋夏令时间上午 10 点至上午 11 点 — 18 次提交,+3,356 行 5 月 11 日,上午 11 点至中午 12 点(太平洋夏令时) — 9 次提交,+861 行 5 月 11 日,下午 12 点至下午 1 点(太平洋夏令时间) — 3 次提交,+412 行 5 月 11 日,下午 1 点至下午 2 点(太平洋夏令时间) — 12 次提交,+2,978 行 5 月 11 日,下午 2 点至下午 3 点(太平洋夏令时间) — 157 次提交,+10,700 行 5 月 11 日,下午 3 点至下午 4 点(太平洋夏令时)— 16 次提交,+1,346 行 5 月 11 日,下午 4 点至下午 5 点(太平洋夏令时) — 3 次提交,+78 行 5 月 11 日,下午 5 点至下午 6 点(太平洋夏令时间) — 41 次提交,+2,568 行 5 月 11 日,下午 6 点至下午 7 点(太平洋夏令时间) — 55 次提交,+4,912 行 5 月 11 日,下午 7 点至晚上 8 点(太平洋夏令时) — 53 次提交,+3,475 行太平洋夏令时 11 日晚上 8 点至晚上 9 点 — 32 次提交,+1,732 行 5 月 11 日晚上 9 点至晚上 10 点(太平洋夏令时间) — 46 次提交,+4,506 行 5 月 11 日,晚上 10 点至晚上 11 点(太平洋夏令时间) — 45 次提交,+1,711 行 5 月 11 日,晚上 11 点至上午 12 点(太平洋夏令时) — 52 次提交,+10,850 行 5 月 12 日太平洋夏令时 12 日上午 12 点至凌晨 1 点 — 30 次提交,+3,760 行 5 月 12 日凌晨 1 点至凌晨 2 点(太平洋夏令时) — 24 次提交,+9,443 行 5 月 12 日上午 2 点至凌晨 3 点(太平洋夏令时间) — 41 次提交,+1,635 行 5 月 12 日上午 3 点至凌晨 4 点(太平洋夏令时间) — 39 次提交,+788 行 5 月 12 日,上午 4 点至凌晨 5 点PDT — 27 次提交,+651 行 5 月 12 日上午 5 点至上午 6 点 PDT — 23 次提交,+779 行 5 月 12 日上午 6 点至上午 7 点 PDT — 1 次提交,+137,576 行 5 月 12 日上午 7 点至上午 8 点 PDT — 2 次提交,+81 行 5 月 12 日上午 8 点至上午 9 点 PDT — 2 次提交,+75 行 5 月 12 日,上午 9 点至上午 10 点(太平洋夏令时) — 2 次提交,+130 行 5 月 12 日,上午 10 点至上午 11 点(太平洋夏令时间) — 5 次提交,+160 行 5 月 12 日,上午 11 点至中午 12 点(太平洋夏令时) — 2 次提交,+ 20 行 5 月 12 日,下午 12 点至下午 1 点(太平洋夏令时间) — 1 次提交,+2 行 5 月 12 日,下午 1 点至下午 2 点(太平洋夏令时)— 30 次提交, +2,677 行 5 月 12 日下午 2 点至 3 点 PDT — 41 次提交,+7,022 行 5 月 12 日下午 3 点至 4 点 PDT — 4 次提交,+200 行 5 月 12 日下午 4 点至下午 5 点 PDT — 27 次提交,+1,423 行 5 月 12 日下午 5 点至下午 6 点 PDT — 19 次提交,+1,055 行 5 月 12 日,下午 6 点至下午 7 点 PDT — 2 次提交,+380 行 5 月 12 日,下午 7 点至晚上 8 点 PDT — 2 次提交,+84 行 5 月 12 日,晚上 9 点至晚上 10 点 PDT — 7 次提交,+273 行 5 月 12 日,晚上 10 点至晚上 11 点 PDT — 3 次提交,+230 行 5 月 12 日,晚上 11 点至上午 12 点 PDT — 7 次提交,+319行数 5 月 13 日,太平洋夏令时间 12 点至凌晨 1 点 — 2 次提交,+133 行 5 月 13 日,上午 1 点至凌晨 2 点(太平洋夏令时间) — 14 次提交,+2,177 行 5 月 13 日,上午 2 点至凌晨 3 点(太平洋夏令时间) — 12 次提交,+685 行 5 月 13 日,上午 4 点至上午 5 点(太平洋夏令时) — 10 次提交,+ 657 行 5 月 13 日,上午 5 点至上午 6 点PDT — 1 次提交,+687 行 5 月 13 日,上午 6 点至上午 7 点 PDT — 11 次提交,+380 行 5 月 13 日,上午 7 点至上午 8 点 PDT — 12 次提交,+5,247 行 5 月 13 日,上午 8 点至上午 9 点 PDT — 14 次提交,+1,051 行 5 月 13 日,上午 9 点至上午 10 点 PDT — 7 次提交,+680 行13 日,上午 10 点至上午 11 点(太平洋夏令时) — 10 次提交,+412 行 5 月 13 日,上午 11 点至下午 12 点(太平洋夏令时) — 6 次提交,+314 行 5 月 13 日,中午 12 点至下午 1 点(太平洋夏令时间) — 10 次提交,+2,980 行 5 月 13 日,下午 1 点至下午 2 点(太平洋夏令时间) — 1 次提交,+0 行 5 月 13 日,下午 2 点至下午 3 点(太平洋夏令时) — 3提交,+439 行 5 月 13 日,下午 5 点至下午 6 点(太平洋夏令时间) — 7 次提交,+114 行 5 月 13 日,下午 6 点至晚上 7 点(太平洋夏令时) — 4 次提交,+605 行 5 月 13 日,晚上 9 点至晚上 10 点(太平洋夏令时) — 1 次提交,+13 行 5 月 13 日,晚上 10 点至晚上 11 点(太平洋夏令时间) — 1 次提交,+48 行 5 月 13 日,晚上 11 点至上午 12 点(太平洋夏令时间) — 1 次提交,+8 行5 月 14 日5 月 14 日,太平洋夏令时 12 点至凌晨 1 点 — 1 次提交,+150 行

端口分支上的每次提交(排除合并),按小时存储。高峰时段:695 次提交。

注意到不一致的时间了吗?我忘记增加运行该实例的 EC2 实例的默认 IOPS。只需一条缓慢的 grep 命令即可使磁盘读写冻结几分钟。

作为工作队列的编译器错误

编写完所有代码后,我要求 Claude 编写一个工作流程来修复每个编译器错误。我们一个箱子一个箱子地走。

≈15,125 个错误

太平洋夏令时 5 月 6 日星期三凌晨 1:29

errors.txt88 修复提交

错误:src/event_loop/SpawnSyncEventLoop.rs

错误:src/sys/lib.rs

错误:runtime/timer/TimerObjectInternals.rs

错误:src/runtime/ffi/ffi_body.rs

错误:src/http/lib.rs

错误:src/js_parser/ast/Parser.rs

错误:JSPromise.rs

错误:standalone_graph/StandaloneModuleGraph.rs

错误:src/http_jsc/websocket_client.rs

错误:doStep5.rs

错误:src/install/PackageManager.rs

平分·64个Claude

工作树1

→→

→→

→→

→→

工作树2

→→

→→

→→

→→

工作树3

→→

→→

→→

→→

工作树4

→→

→→

→→

→→

1 个修复2 个审核1 个适用

→ 每箱承诺土地

bun_bundler0

bun_js_parser17

小圆面包_css10

小圆面包_http3

面包_sys3

bun_alloc1

bun_sourcemap3

小圆面包_ini0

bun_analytics1

小圆面包_zlib0

· 阶段 d(sql_jsc/mysql/protocol):修复导入、ReaderContext 边界、WTFStringImpl 帮助程序

· 阶段 d(tier0):bun_sourcemap parse_json:BabyList.len 字段,Option<StoreRef> 展开

· 阶段 d(jpa):skipTypescript.rs — 移植实体,删除 _draft/todo 存根

D 阶段如何工作,从其 1,610 个真实提交(太平洋夏令时间 5 月 6 日)重放:cargo check 将 大约 16,000 个错误写入文件,按 crate 分组;工作流程将它们分配给 64 个 Claude,即 4 个工作树上的 16 个循环,每个 Claude 进行修复,两个进行审查,一个进行应用。每个芯片都是一批真正的提交:它落在其实际的板条箱上,然后计数器才会移动。错误行是真正的提交主题。

最棘手的错误是循环依赖。

我们的 Zig 代码库是一个编译单元(实际上是一个 crate)。我想将新的 Rust 代码库拆分为 ~100 个板条箱,以便 Rust 编译速度更快,但这需要避免循环依赖,同时最大限度地减少与原始 Zig 实现相比的更改。 我的 PR 在开始 Rust 重写之前立即执行此操作是不够的。我没有重新开始,而是运行了另一个工作流程来对具有循环依赖关系的代码进行分类并将其全部写下来,然后运行另一个工作流程来进行重构。

修复循环依赖关系发现大约 16,000 个编译器错误。对于 1 个人来说这是一个巨大的数字,但同时对于 64 个Claude来说并不是一个疯狂的数字。

为了最大限度地提高并行性,工作流在每个 crate 上循环。

  • 对于每个 crate,运行 cargo check,按文件对输出进行分组并将错误保存到文件
  • 修复该包中的所有编译器错误
  • 2 个针对板条箱更改的敌对审阅者
  • 1 个修复程序应用修复

为了防止Claude互相踩踏,cargo check只在开始时运行,并且像其他运行一样,直到最后才运行git

Another false start

Claude 将“让我们编译所有的包”解释为“删除有编译错误的函数”。Claude还开始在文档变通办法中添加可疑的长解释性注释,因此我添加了这条规则以供敌对审阅者拒绝:

如果您需要一段长的注释来证明解决方法可行的原因,则代码是错误的 - 请修复代码。

一次快速编辑,几个小时后,这些事情就不再发生了。

冒烟测试

模特们喜欢说“冒烟测试”

一旦cargo check通过,接下来就是编译并运行bun --version。它有链接器错误。然后,它在启动时立即出现恐慌。

下一个目标是让它运行bun test <file>。一旦成功,我们就可以开始运行测试了!是时候进行另一个工作流程了,循环 Bun 的 CLI 子命令:

  • 将每个失败的堆栈跟踪及其子命令保存到文件中
  • 对于按子命令分组的每个失败堆栈跟踪,进行 1 个 Claude 修复
  • 2 个敌对审稿人
  • 1 个修复者应用了建议

让测试套件在本地通过

此工作流程在测试文件上循环。

运行大约 100 个随机测试文件,这些文件按代码库中的文件夹分片到 4 个工作树之一。对于每个失败的测试,将堆栈跟踪和错误保存到文件中,1 个实施者提出修复方案,2 个敌对审阅者,然后 1 个修复者应用。

更多错误的开始

我们的测试套件有大量内存泄漏测试和一些可能需要一分钟以上的集成测试 - 例如:运行 next dev 并检查热模块重新加载的测试可以拾取更改 100 次。其中一些测试在调试版本中超时。

我们还进行了压力测试,用于耗尽计算机上最大数量的 TCP 套接字、对磁盘读取和写入 GB 字节的测试,以及生成约 10k 进程的测试。

这需要比“请”更强的隔离,因此我们使用 systemd-run (cgroups) 来限制内存和 CPU 使用并隔离 pid 命名空间。机器磁盘空间不足并且崩溃了好几次。

获取 CI 中通过的测试套件

第一次 CI 运行两天后,失败列表从 972 个测试文件减少到 23 个。一天半后,Linux 完全变绿 — 第一次,感觉这个 Rust 重写实际上可以发挥作用。

0 / 6 平台绿色

版本 #53047 · 太平洋夏令时 5 月 9 日星期六上午 11:52

macOS x64 · 2 个分片

build #52897:碎片故障build #52932:碎片故障build #52934:碎片故障build #52938:碎片故障build #52944:碎片故障build #52946:碎片故障build #52949:碎片故障build #52975:碎片故障build #52998:碎片故障build #53007:碎片故障build#53015:碎片故障build#53026:碎片故障build#53027:碎片故障build#53035:碎片故障build#53041:碎片故障build#53047:碎片故障build#53056:碎片故障build#53077:碎片故障build#53090:碎片故障build #53095:碎片故障build #53106:碎片故障build #53109:碎片故障build #53123:碎片故障build #53127:碎片故障build #53130:碎片故障build #53131:碎片故障build #53133:碎片故障build #53134:碎片故障build #53143:分片失败build #53149:所有分片均已通过build #53159:所有分片均已通过build #53164:所有分片均已通过build #53167:所有分片均已通过build #53172:所有分片均已通过build #53176:所有分片均已通过build #53194:所有分片均已通过build #53208:所有分片均已通过build #53213:分片失败build#53214:分片失败build#53216:没有失败(部分运行)build#53222:所有分片都通过了build#53229:没有失败(部分运行)build#53241:所有分片都通过了build#53265:所有分片都通过了build#53271:所有分片都通过了build#53304:碎片故障build #53327:碎片故障build #53340:碎片故障build #53401:碎片故障build #53431:碎片故障build #53491:所有碎片通过build #53503:所有碎片通过build #53748:碎片故障build #53753:所有碎片通过build #53787:全部分片通过build #53811:所有分片通过build #53933:没有失败(部分运行)build #53952:所有分片通过build #53983:分片失败build #53992:分片失败build #53999:分片失败build #54012:所有分片通过build #54015:所有分片通过build #54017:所有分片通过build #54022:分片失败build #54026:分片失败build #54033:所有分片通过build #54040:所有分片通过build #54047:分片失败build #54049:分片失败build #54057:分片失败build #54064:所有分片通过build #54074:所有分片通过build#54093:所有分片通过build#54144:没有失败(部分运行)build#54161:分片失败build#54186:分片失败build#54189:分片失败build#54196:分片失败build#54202:所有分片通过

Linuxarm64·60个分片

build #52934:碎片故障build #52938:碎片故障build #52944:碎片故障build #52969:碎片故障build #52975:碎片故障build #52980:碎片故障build #52988:碎片故障build #52996:碎片故障build #52998:碎片故障build #53007:碎片故障build#53013:碎片故障build#53014:碎片故障build#53015:碎片故障build#53026:碎片故障build#53027:碎片故障build#53031:无故障(部分运行)build#53032:碎片故障build#53035:碎片故障build#53041:碎片故障build #53047:碎片故障build #53056:碎片故障build #53059:碎片故障build #53077:碎片故障build #53083:碎片故障build #53086:碎片故障build #53090:碎片故障build #53095:碎片故障build #53106:碎片故障build #53109:碎片故障build#53123:碎片故障build#53127:碎片故障build#53130:碎片故障build#53131:碎片故障build#53133:碎片故障build#53134:碎片故障build#53135:碎片故障build#53143:碎片故障build#53149:碎片failurebuild #53159: 分片故障build #53164: 分片故障build #53167: 所有分片均已通过build #53172: 分片故障build #53176: 所有分片均已通过build #53188: 分片故障build #53194: 分片故障build #53208: 所有分片均已通过build #53212: 分片故障build #53213:分片失败build#53214:分片失败build#53216:所有分片通过build#53222:所有分片通过build#53229:所有分片通过build#53236:没有失败(部分运行)build#53241:所有分片通过build#53260:所有分片通过build#53265:全部分片已通过build #53271:所有分片已通过build #53280:无故障(部分运行)build #53298:无故障(部分运行)build #53304:分片故障build #53327:所有分片已通过build #53340:所有分片已通过build #53360:无故障(部分运行)build #53419:分片故障build #53431:碎片故障build#53458:无故障(部分运行)build#53485:碎片故障build#53491:碎片故障build#53503:碎片故障build#53514:无故障(部分运行)build#53570:碎片故障build#53583:碎片故障build#53599:碎片故障build #53748: 分片故障build #53753: 所有分片均已通过build #53762: 无故障(部分运行)build #53787: 无故障(部分运行)build #53811: 所有分片均已通过build #53852: 无故障(部分运行)build #53863: 分片故障build #53893: 分片故障build #53914: 全部分片已通过build #53933:所有分片已通过build #53952:所有分片已通过build #53983:分片故障build #53992:分片故障build #53999:分片故障build #54008:无故障(部分运行)build #54012:所有分片已通过build #54015:所有分片已通过build #54017:所有分片均已通过build #54022:分片故障build #54026:所有分片均已通过build #54030:分片故障build #54033:所有分片均已通过build #54040:所有分片均已通过build #54047:分片故障build #54049:分片故障build #54055:分片故障build #54057: 分片失败build #54064: 所有分片均已通过build #54074: 所有分片均已通过build #54083: 无故障(部分运行)build #54093: 所有分片均已通过build #54144: 所有分片均已通过build #54161: 分片故障build #54186: 分片故障build #54189: 分片failurebuild #54196:分片失败build #54202:所有分片均通过

Linux x64 · 60 个分片

build #52934:碎片故障build #52938:碎片故障build #52944:碎片故障build #52969:碎片故障build #52975:碎片故障build #52988:碎片故障build #52996:碎片故障build #52998:碎片故障build #53007:碎片故障build #53013:碎片故障build#53014:碎片故障build#53015:碎片故障build#53026:碎片故障build#53027:碎片故障build#53032:碎片故障build#53033:碎片故障build#53035:碎片故障build#53041:碎片故障build#53047:碎片failurebuild #53056:分片故障build #53059:无故障(部分运行)build #53077:分片故障build #53083:无故障(部分运行)build #53086:分片故障build #53090:分片故障build #53095:分片故障build #53106:分片故障build #53109:分片故障build #53123:碎片故障build#53127:碎片故障build#53130:碎片故障build#53131:碎片故障build#53133:碎片故障build#53134:碎片故障build#53135:碎片故障build#53143:碎片故障build#53149:碎片故障build#53159:碎片failurebuild #53164: 分片失败build #53167: 所有分片均通过build #53172: 所有分片均通过build #53176: 所有分片均通过build #53188: 分片失败build #53194: 所有分片均通过build #53208: 所有分片均通过build #53212: 分片失败build #53213: 分片failurebuild #53214: 分片失败build #53216: 所有分片均已通过build #53222: 所有分片均已通过build #53229: 所有分片均已通过build #53236: 无故障(部分运行)build #53241: 所有分片均已通过build #53260: 所有分片均已通过build #53265: 所有分片均已通过build #53271:所有分片都通过了build#53280:没有失败(部分运行)build#53304:分片失败build#53327:所有分片都通过了build#53340:所有分片都通过了build#53360:没有失败(部分运行)build#53419:没有失败(部分运行)build#53431:分片失败build#53458:没有失败(部分运行)build #53485:分片失败build #53491:所有分片都已通过build #53503:所有分片都已通过build #53514:没有失败(部分运行)build #53570:分片失败build #53583:分片失败build #53599:分片失败build #53748:分片失败build #53753:所有分片均已通过build #53759:无故障(部分运行)build #53781:分片故障build #53787:无故障(部分运行)build #53811:所有分片均已通过build #53863:分片故障build #53893:分片故障build #53914:所有分片均已通过build #53933:分片故障build #53952:所有分片通过build #53983:分片失败build #53992:分片失败build #53999:分片失败build #54008:没有失败(部分运行)build #54012:所有分片通过build #54015:所有分片通过build #54017:所有分片通过build #54022:分片failurebuild #54026:所有分片均已通过build #54030:分片故障build #54033:所有分片均已通过build #54040:无故障(部分运行)build #54047:分片故障build #54049:分片故障build #54055:分片故障build #54057:分片故障build #54064:所有分片passbuild #54074:所有分片都已通过build #54083:没有失败(部分运行)build #54093:所有分片都已通过build #54144:所有分片都已通过build #54161:分片故障build #54186:分片故障build #54189:分片故障build #54196:分片故障build #54202:全部分片已通过

macOS arm64 · 4 个分片

build #52897:碎片故障build #52929:碎片故障build #52932:碎片故障build #52944:碎片故障build #52975:碎片故障build #52996:碎片故障build #52998:碎片故障build #53007:碎片故障build #53013:碎片故障build #53014:碎片故障build#53015:碎片故障build#53026:碎片故障build#53027:碎片故障build#53032:碎片故障build#53035:碎片故障build#53041:碎片故障build#53047:碎片故障build#53056:碎片故障build#53059:碎片故障build #53077:碎片故障build #53095:碎片故障build #53109:碎片故障build #53123:碎片故障build #53127:碎片故障build #53130:碎片故障build #53131:碎片故障build #53133:碎片故障build #53134:碎片故障build #53135:碎片故障build #53143:碎片故障build #53149:碎片故障build #53159:碎片故障build #53164:碎片故障build #53167:碎片故障build #53172:碎片故障build #53176:碎片故障build #53188:碎片故障build #53194:碎片故障build #53208:碎片故障build#53212:碎片故障build#53213:碎片故障build#53214:碎片故障build#53216:碎片故障build#53222:碎片故障build#53229:没有失败(部分运行)build#53236:没有失败(部分运行)build#53241:所有分片都通过了build #53265:所有分片都通过build#53271:没有失败(部分运行)build#53280:没有失败(部分运行)build#53304:分片失败build#53327:没有失败(部分运行)build#53340:没有失败(部分运行)build#53360:没有失败(部分运行)build#53368:分片失败build #53379:碎片故障build#53383:碎片故障build#53401:碎片故障build#53431:碎片故障build#53458:碎片故障build#53491:无故障(部分运行)build#53503:碎片故障build#53570:碎片故障build#53583:碎片故障build#53599:分片故障build #53601:分片故障build #53748:分片故障build #53753:所有分片均已通过build #53757:无故障(部分运行)build #53759:无故障(部分运行)build #53787:无故障(部分运行)build #53811:所有分片均已通过build #53952:无故障(部分运行)build #53992: 分片故障build #53999: 分片故障build #54007: 无故障(部分运行)build #54012: 所有分片均已通过build #54015: 分片故障build #54017: 所有分片均已通过build #54022: 分片故障build #54026: 无故障(部分运行)build #54030: 分片故障build #54033:所有分片通过build#54040:分片故障build#54047:分片故障build#54049:分片故障build#54055:分片故障build#54057:分片故障build#54064:分片故障build#54074:分片故障build#54093:分片故障build#54161:分片故障build #54186:分片故障build #54189:分片故障build #54196:分片故障build #54202:所有分片均通过

Windows x64 · 8 个分片

build #53090:碎片故障build #53094:碎片故障build #53095:碎片故障build #53106:碎片故障build #53109:碎片故障build #53123:碎片故障build #53127:碎片故障build #53130:碎片故障build #53131:碎片故障build #53133:碎片故障build#53134:碎片故障build#53135:碎片故障build#53143:碎片故障build#53149:碎片故障build#53159:碎片故障build#53164:碎片故障build#53167:碎片故障build#53172:碎片故障build#53176:碎片故障build #53188:碎片故障build #53194:碎片故障build #53208:碎片故障build #53212:碎片故障build #53213:碎片故障build #53214:碎片故障build #53216:碎片故障build #53222:碎片故障build #53229:碎片故障build #53236:分片故障build #53241:分片故障build #53260:分片故障build #53265:分片故障build #53271:分片故障build #53280:分片故障build #53298:分片故障build #53304:分片故障build #53327:所有分片均已通过build #53340:所有分片均已通过build #53360:没有失败(部分运行)build#53419:碎片故障build#53431:碎片故障build#53458:碎片故障build#53470:没有失败(部分运行)build#53485:碎片故障build#53491:没有失败(部分运行)build#53503:碎片故障build#53514:没有失败(部分运行)运行)build#53565:没有失败(部分运行)build#53570:碎片故障build#53599:碎片故障build#53745:碎片故障build#53748:碎片故障build#53753:碎片故障build#53757:碎片故障build#53759:碎片故障build#53762:碎片故障build #53769:碎片故障build#53781:碎片故障build#53787:碎片故障build#53808:碎片故障build#53811:碎片故障build#53852:碎片故障build#53863:碎片故障build#53883:碎片故障build#53893:碎片故障build#53914:全部分片已通过build #53933:所有分片已通过build #53952:所有分片已通过build #53973:无故障(部分运行)build #53983:分片故障build #53992:分片故障build #53999:分片故障build #54002:无故障(部分运行)build #54004:无故障(部分运行)build #54007: 分片故障build #54008: 无故障(部分运行)build #54012: 所有分片均已通过build #54015: 所有分片均已通过build #54017: 所有分片均已通过build #54022: 分片故障build #54026: 所有分片均已通过build #54030: 所有分片均已通过build #54033: all分片已通过build #54040:所有分片已通过build #54047:分片故障build #54049:分片故障build #54055:分片故障build #54057:分片故障build #54064:所有分片已通过build #54074:所有分片已通过build #54083:所有分片已通过build #54093:全部分片已通过build #54144:分片故障build #54161:分片故障build #54186:分片故障build #54189:分片故障build #54196:分片故障build #54202:所有分片均已通过

Windowsarm64·8个分片

build #53090:碎片故障build #53095:碎片故障build #53106:碎片故障build #53109:碎片故障build #53123:碎片故障build #53127:碎片故障build #53130:碎片故障build #53131:碎片故障build #53134:碎片故障build #53135:碎片故障build#53149:碎片故障build#53159:碎片故障build#53164:碎片故障build#53167:碎片故障build#53172:碎片故障build#53176:碎片故障build#53188:碎片故障build#53194:碎片故障build#53208:碎片failurebuild #53212:分片故障build #53213:分片故障build #53214:分片故障build #53216:分片故障build #53222:分片故障build #53229:分片故障build #53236:无故障(部分运行)build #53241:分片故障build #53260:分片故障build #53265: 分片故障build #53271: 分片故障build #53304: 分片故障build #53327: 所有分片均已通过build #53340: 所有分片均已通过build #53360: 无故障(部分运行)build #53419: 无故障(部分运行)build #53431: 分片故障build #53458: 无故障(部分)运行)构建#53485:碎片故障构建#53491:无故障(部分运行)构建#53503:无故障(部分运行)构建#53599:碎片故障构建#53748:碎片故障构建#53753:碎片故障构建#53757:碎片故障构建#53759:碎片故障构建#53762:碎片故障构建#53787:碎片故障build#53808:碎片故障build#53811:碎片故障build#53852:碎片故障build#53863:碎片故障build#53883:碎片故障build#53893:碎片故障build#53914:没有失败(部分运行)build#53933:所有碎片通过build #53952:所有分片通过build #53983:分片失败build #53992:分片失败build #53999:分片失败build #54007:没有失败(部分运行)build #54012:所有分片通过build #54015:所有分片通过build #54017:所有分片通过build #54022:分片failurebuild #54026:所有分片均已通过build #54030:无故障(部分运行)build #54033:所有分片均已通过build #54040:所有分片均已通过build #54047:分片故障build #54049:分片故障build #54055:无故障(部分运行)build #54057:分片故障build #54064:全部分片已通过build #54074:所有分片已通过build #54083:无故障(部分运行)build #54093:所有分片已通过build #54144:分片故障build #54161:分片故障build #54186:分片故障build #54189:分片故障build #54196:分片故障build #54202:全部分片已通过

每个 CI 构建的测试分片(按平台)跨 135 个运行测试的构建(420 个从 BuildKite 中挖掘)。亮绿色:每个碎片都通过了。暗绿色:没有失败,但运行被缩短(被取代)。红色:至少有一个分片发生故障。当其完整套件首次通过时,每个通道都会被标记——Linux 的 60 个分片比 Windows 早几乎一整天都是绿色的。平台一直呈红色摇晃,直到最后一次失败的测试落下;最终的全绿色版本是#54202。

合并之前的其余时间很简单。循环修复每个平台的 CI 测试失败的工作流程,直到不再出现测试失败。用于 Windows 相关清理的多个工作流程,用于删除重复代码、减少不安全使用以及一般清理一些代码。

合并 Rust 重写

一旦 Bun 的测试套件 100% 在所有平台上通过 CI(并且我手动验证了测试实际上正在运行并且没有被跳过),我在本地运行了一堆命令来测试东西 - 然后我按下了合并按钮。

合并到 main 不是版本控制的版本。此时,我有足够的信心继续前进并致力于重写,但还没有足够的信心来发布它。

统计信息

在高峰期,我们同时运行 4 个工作流程,每个工作流程都在一个单独的工作树中,每个工作流程有 16 个 Claudes。一次大约 64 个Claude。

git log · claude/phase-a-portpeak:一分钟内 58 次提交

52

提交

+796,601

已写入的行,包括重写

太平洋夏令时间 5 月 5 日星期二中午 12:39

第一个 100 个文件草稿批次PR #30412 opensmerged

·a阶段:草稿批次head-1(100个文件)+12,379−0

· b1 阶段:路径 + 字符串编译(门草稿;最小编码/字符串/词法分析器表面)+2,312−2,149

所有 6,502 次提交(不包括合并)均已重放。粉色条大多是新代码;青色条大部分是删除。行计数器对沿途的每次重写进行计数——到达的差异是+1,009,272。日志是真实的提交消息。

0 个测试已跳过或删除

11 天(5 月 3 日 → 5 月 14 日合并)· 6,778 次提交

平台expect() 调用测试文件
Debian 13 x641,386,82660,6244,174
macOS 14 arm641,259,95358,8504,175
Windows 2019 x641,007,54457,3374,173

合并前,这需要 59 亿个未缓存的输入令牌、6.9 亿个输出令牌和 720 亿个缓存的输入令牌读取 — 按 API 定价约为 165,000 美元。从手工角度来看,我认为这需要 3 名具有代码库完整背景的工程师大约一年的时间,在此期间我们将无法提高 Node.js 兼容性、修复错误、修复安全问题或实现新功能。我们永远不会那样做。现实的选择是什么也不做,并永远修复本文顶部的错误。

这是当今可能实现的最前沿。我使用了 Claude Fable 5 的预发行版本,这是一个 Mythos 级模型。 Claude Code 的动态工作流程使 64 个 Claudes 运行了 11 天(否则我必须编写自己的工具才能实现这一点)。

工作继续

自合并 Rust 端口以来,我们已完成 Claude Code 安全 的 11 轮安全审查并解决了调查结果。

我们还为 Bun 中的每个解析器添加了 24/7 覆盖引导模糊测试 - JavaScript、TypeScript、JSX、CSS、JSON5、JSONC、TOML、YAML、Markdown、INI、Bun Shell 脚本、semver 范围、.patch 文件和 CSS 颜色。模糊器会自动将其发现的错误发送给 Claude,以提交 PR 重现和修复,然后由人工审核 PR。到目前为止,它已经执行了我们的解析器 1000 亿次,从而产生了大约 15 个 PR。

在撰写本文时,大约 4% 的 Bun 的 Rust 代码位于 unsafe 块内(~13,000 个 unsafe 关键字跨 ~27,000 行/~780,000 行),并且这些块中的 78% 是单行 — 来自 C++ 的指针,或对 C 库的一次调用。我预计这个数字会随着时间的推移而下降,因为我们从忠实的 Zig 端口(没有 grepable unsafe 关键字)重构为惯用的 Rust,但我们将继续使用像 JavaScriptCore 这样的 C 和 C++ 库,因此它总是比纯 Rust 项目拥有更多的 unsafe

移植错误

Rust 重写的重点是稳定性,但不可能进行像这样的大规模更改并引入零回归。

此重写引入了 19 个已知的回归,每一个都已得到修复。

大多数回归都来自两种语言中语法相同但语义不同的代码。

debug_assert! 内的副作用

这两个片段看起来相似,但行为不同。 Zig 的 assert 是一个函数,因此它的参数在每个构建中运行。 Rust 的 debug_assert! 是一个宏,因此在发布版本中,整个 expression 都被删除,包括 insert_stale 调用。

// Zig:
if (dev.framework.react_fast_refresh) |rfr| {
    assert(try dev.client_graph.insertStale(rfr.import_source, false) == IncrementalGraph(.client).react_refresh_index);
}

// Rust:
if let Some(rfr) = &dev.framework.react_fast_refresh {
    debug_assert!(dev.client_graph.insert_stale(&rfr.import_source, false)? == react_refresh_index);
}

insert_stale 将文件添加到前端开发服务器的热重载图表中。在发布版本中,它停止运行,并且在某些情况下,对于具有使用 React 的 HTML 路由的项目,HMR 会中断,而热重新加载的文件会失效:Cannot destructure property 'isLikelyComponentType' of 'k'。调试构建有效。 #30678

Slices of odd length

Bun 的 Zig 帮助器 reinterpretSlice(u16, bytes)(支持切片的内置强制转换)使用了 @divTrunc 并忽略了尾随奇数字节。 bytemuck::cast_slice 反而对此感到恐慌。 Blob.text() 在 UTF-16 字节顺序标记上,后跟奇数个字节,停止返回字符串并使进程陷入恐慌。我们又继续忽略奇数字节:&buf[..buf.len() & !1]#31188

边界检查

在 macOS 和 Linux 上,我们使用 ReleaseFast 编译了 Bun 的 Zig 代码,这消除了边界检查。 Rust 的发布版本保留了它们。

Bun 的模块解析器将长文件名实习到溢出块的全局列表中。原始的 Zig 代码将每个块的大小设置为 count / 4 或 2048。端口留下了一个占位符:

/// ... so use a nonzero stand-in until Phase B threads the
/// per-instantiation value through.
pub const BSS_OVERFLOW_BLOCK_SIZE: usize = 64;

这将上限从 840 万个实习文件名降低到了实际项目所达到的 270,272 个,并使我们从 Zig 移植的 ptrs[4095] 可以访问。 Rust 惊慌失措而不是写到最后。如果我们使用 ReleaseSafe (我们只在 Windows 上这样做),在这种情况下 Zig 也会出现恐慌。 #31503

comptime 格式字符串

Output.pretty<r><d> 颜色标记重写为 ANSI 转义。在 Zig 中,fmtcomptime,因此在替换参数之前标记就消失了。 Rust 函数没有 comptime 参数,因此 Output::pretty 只能看到完成的字符串,并重写参数上的标记。

// Zig:
pub inline fn pretty(comptime fmt: string, args: anytype) void;
Output.pretty("<r>{f}<r>", .{hyperlink});

// Rust:
pub fn pretty(payload: impl PrettyFmtInput);
Output::pretty(format_args!("<r>{}<r>", hyperlink));

bun update -i 将包名称打印为 OSC 8 超链接,以 ESC \ 终止。该反斜杠位于尾随 <r>< 之前,标记解析器会吃掉它,并且 r 将打印为文本。

在Rust中它必须是一个宏:bun_core::pretty!("<r>{}<r>", hyperlink)#30693

Bun 在 Rust 中更好

到目前为止,Bun v1.4.0 修复了 v1.3.14 中重现的 128 个错误。这些问题包括内存泄漏、崩溃、帮助文本颜色错误等。

减少内存使用

Rust有一个强大的语言级清理内存的工具:Drop。实现Drop后,每次值超出范围时都会自动调用drop函数。

impl Drop for Bytes {
    fn drop(&mut self) {
        if !self.pinned.is_empty() {
            JSC__JSValue__unpinArrayBuffer(self.pinned);
        }
    }
}

在 Zig 中,defer 可用于在作用域末尾运行代码:

const bytes: ArrayBuffer = try .fromPinned(global, value);
defer bytes.unpin();

在 Zig 中,需要将 defer 添加到可能需要清理的每个单独的调用站点。很容易最终忘记清理(内存泄漏),或者在很少到达的错误处理代码中运行清理代码两次(双重释放)。在 Rust 中,当该值不再可访问时,Drop 会自动运行 - 交换“无隐藏控制流”来防止常见的 footgun。

Drop 修复了 Bun 中与错误处理代码中的文件路径相关的多个内存泄漏。

我们修复了所有可检测的内存泄漏

我们改进了 Bun 的 LeakSanitizer 集成,以跟踪所有本机代码内存分配

下面是一个示例:每个进程内 Bun.build() 调用都会泄漏数兆字节的内存 - 已解析的源文本和 AST 符号表比它们所属的构建寿命更长。

// Bundle the same 60-module project 2,000 times in one process
for (let i = 0; i < 2_000; i++) {
  await Bun.build({
    entrypoints: ["./index.js"],
    minify: true,
    sourcemap: "external",
  });
}

在 Bun v1.3.14 中,每个构建都会永远泄漏约 3 MB - 捆绑每个请求的开发服务器等工具最终会耗尽内存。在 Bun v1.4.0 中,内存逐渐减少:

构建Bun v1.3.14Bun v1.4.0
5001,914 MB526 MB
1,0003,506 MB586 MB
1,5005,097 MB608 MB
2,0006,745 MB609 MB

在 Zig 中执行此操作的先前尝试 未合并,因为缺少 Drop 的等效项使得更难以自信地进行合并。

较小的二进制大小

Rust 重写中的初始更改将 Windows 上的二进制文件大小减少了 3.8 MB,在 macOS 上减少了 5.5 MB,在 Linux 上减少了 6.8 MB。这主要是因为我们在 Zig 代码中使用了太多 comptime

在最初的缩小之后,团队探索了更多减少二进制大小的机会,使用链接器优化(例如相同代码折叠)、从 ICU 中删除未使用的数据以及按需使用 zstd 字典延迟解压缩 libicu 的小部分。

结合 Rust 重写、ICU 更改和相同的代码折叠,Bun 的二进制大小在 Linux 和 Windows 上缩小了 ~20%

版本平台尺寸
Bun v1.4.0(金丝雀)Windows76 MB
Bun v1.3.14Windows94 MB
Bun v1.4.0(金丝雀)Linux70 MB
Bun v1.3.14Linux88 MB

减少堆栈空间使用

TOML 解析器以及 Bun 中的所有其他递归下降解析器(JSON、YAML、JavaScript、TypeScript 等)现在使用更少的堆栈空间。

这在合并 Rust 重写之前导致了一些测试失败:

bun test v1.3.14-canary.1 (e99311e58)
.......

105 | });
106 |
107 | it("Bun.TOML.parse throws on deeply nested inline tables instead of crashing", () => {
108 |   const depth = 25_000;
109 |   const deepToml = "a = " + "{ b = ".repeat(depth) + "1" + " }".repeat(depth);
110 |   expect(() => Bun.TOML.parse(deepToml)).toThrow(RangeError);
                                               ^
error: expect(received).toThrow(expected)

Expected constructor: RangeError

Received function did not throw
Received value: {
  a: {
    b: {
      b: {
        b: {
          b: {
            b: {
              b: {
                b: {
                  b: [Object ...],
                },
              },
            },
          },
        },
      },
    },
  },
}

      at <anonymous> (/var/lib/buildkite-agent/build/test/js/bun/resolve/toml/toml.test.js:110:42)

✗ Bun.TOML.parse throws on deeply nested inline tables instead of crashing [2907.64ms]

Rust 的 LLVM IR 代码生成器会发出 LLVM 的 llvm.lifetime.startllvm.lifetime.end 内在函数,这让 LLVM 可以重用堆栈空间槽。这使得具有嵌套作用域的大型函数使用显着更少的堆栈空间。

之前,我们通过将特别大的函数重构 为许多较小的函数来手动解决一个未解决的问题

速度提高 2% - 5%

Rust 支持 C/C++ 和 Rust 之间的跨语言链接时优化,从而实现跨编程语言的内联(这太酷了!!)。

我们在 Linux x64(EC2、Xeon Platinum 8488C)上对 Bun v1.3.14 与 Bun v1.4.0 进行了基准测试。 HTTP 吞吐量使用 oha 针对 hello-world 服务器测量,应用工作负载使用 hyperfine 测量。

HTTP 吞吐量(req/s,3 轮的平均值)

服务器Bun v1.3.14Bun v1.4.0Δ
Bun.serve169.6k177.7k+4.8%
node:http103.8k108.5k+4.5%
Elysia158.9k163.3k+2.8%
express64.5k66.6k+3.2%
fastify91.5k95.9k+4.8%

应用程序/CLI (hyperfine)

工作负载Bun v1.3.14Bun v1.4.0Δ
next build13.62 s13.03 s+4.5%
vite build(tsc + vite)1.69 s1.65 s+2.2%
tsc -b --force0.94 s0.89 s+4.7%

生产

Prisma 在 Bun 的 Rust 重写上推出了 Prisma Compute 公共测试版。

“我们遇到了内存泄漏和连接池在虚拟机暂停和恢复后无法恢复的问题。当 Rust 重写出现时,我们针对相同的故障模式对其进行了测试。它完美地处理了这些问题。” - 阿列克谢·奥连科

Claude Code v2.1.181(6 月 17 日发布)及更高版本使用 Bun 的 Rust 端口。 Linux 上的启动速度提高了 10%,但除此之外几乎没有人注意到。无聊是好事。

运费

Bun v1.3.14 是用 Zig 编写的 Bun 的最后一个版本。 Bun v1.4.0 将是用 Rust 编写的 Bun 的第一个版本。它现已在 Canary 中提供 - 请报告您发现的任何问题:

bun upgrade --canary

可维护性

对于我自己和团队来说,我们的新 Rust 代码库感觉与旧的 Zig 代码库非常相似。例如,以下是原始 Zig 代码和新 Rust 代码的片段:

pub fn canMergeSymbols(
    scope: *Scope,
    existing: Symbol.Kind,
    new: Symbol.Kind,
    comptime is_typescript_enabled: bool,
) SymbolMergeResult {
    if (existing == .unbound) {
        return .replace_with_new;
    }

    if (comptime is_typescript_enabled) {
        // In TypeScript, imports are allowed to silently collide with symbols within
        // the module. Presumably this is because the imports may be type-only:
        //
        //   import {Foo} from 'bar'
        //   class Foo {}
        //
        if (existing == .import) {
            return .replace_with_new;
        }

        // ...
    }

    // ...
}
pub fn can_merge_symbol_kinds<const IS_TYPESCRIPT_ENABLED: bool>(
    scope_kind: Kind,
    existing: symbol::Kind,
    new: symbol::Kind,
) -> SymbolMergeResult {

    if existing == symbol::Kind::Unbound {
        return SymbolMergeResult::ReplaceWithNew;
    }

    if IS_TYPESCRIPT_ENABLED {
        // In TypeScript, imports are allowed to silently collide with symbols within
        // the module. Presumably this is because the imports may be type-only:
        //
        //   import {Foo} from 'bar'
        //   class Foo {}
        //
        if existing == symbol::Kind::Import {
            return SymbolMergeResult::ReplaceWithNew;
        }

        // ...
    }

    // ...
}

任何理解原始 Zig 代码的人都可以理解机械翻译的 Rust 代码。我通过检查对抗性代码审查代理是否正确捕获了 Zig 代码和 Rust 代码之间的差异来审查了原始的 Rust 重写 PR,确保他们确保遵循移植指南和生命周期指南,并且自己手动阅读大量代码与 Zig 与 Rust 并排。

下一步

Bun v1.4 使 Bun 更快、更小、使用更少的内存,并为团队提供了极其强大的工具来系统地提高未来的稳定性:Rust 的借用检查器、Miri(在 CI 中运行不断增长的代码块)、LeakSanitizer 和用于解析器的 24/7 覆盖引导模糊测试。虽然还有更多需要重构,但一切已经有了一个良好的开端。

这个 Rust 重写需要一个具有代码库完整背景的工程师团队花费一年的时间。 1 名工程师使用 Fable 并密切监控 Claude Code,我们在 11 天内从开始到测试套件在所有平台上 100% 通过。

今天,一名工程师可以比一年前做更多的事情。

著作权归原作者及相关权利人所有。如需更正或删除, 请联系参识AI