Bun の Rust 移植:11日間、6,778コミット、そして実際に何が変わったか
Bun のチームは 2026 年 5 月、珍しいことをやった。ランタイムのネイティブの中核をまるごと Rust 版に置き換えたのだ。しかも 11 日で。
冒頭の開示が全体のトーンを決めている。Bun は 2025 年 12 月に Anthropic に買収された。創設者の Jarred Sumner は、リリース前の Claude —— 彼らが「Mythos 級モデル」と呼ぶ Fable 5 —— を作業の大部分に使った。これは思考実験ではない。最初の Rust 版である Bun v1.4.0 は今、canary で配信されている。
生の数字はこうだ。
- Zig の 535,496 行(コメント除く)が Rust のコードベースになった
- 5 月 3 日から 5 月 14 日までに 6,778 コミット
- 1,448 個の
.zigファイルが機械的に.rsへ移植 - API 利用料は約 $165,000(キャッシュ外入力トークン 5.9B、出力 690M)
- ピーク時は 64 個の Claude が同時に 4 つの worktree で稼働

Bun はそもそも Zig への賭けだった
Bun は、esbuild の JS/TS トランスパイラを Go から Zig へ一行ずつ移植したところから始まった。Sumner が Zig の一行目を書いたのは 2021 年 4 月 16 日、Hacker News で Zig の一枚紙の言語リファレンスを見てからだった。初期バージョン —— トランスパイラ、ミニファイア、バンドラ、npm 互換のパッケージマネージャ、Jest 風のテストランナー、Node.js の API 互換層 —— は、LLM がこういう仕事に使えるようになる前に、一人で一年かけて、手狭なオークランドのアパートで書かれた。
その賭けは実を結んだ。Bun の CLI は今、月間 2,200 万ダウンロードを超える。Claude Code と OpenCode がランタイムとして使っている。Vercel、Railway、DigitalOcean が公式サポートを提供している。
ただ、その規模こそが問題でもあった。
負債:GC メモリの隣にある手動メモリ
JavaScript はガベージコレクション方式だ。Bun は JavaScriptCore(Safari のエンジン)に加え、C/C++ のライブラリを山のように埋め込んでいる。uWebSockets、BoringSSL、SQLite、lsquic だ。Zig は C と同じく、メモリ管理を自動ではやってくれない。Bun の役割は、GC される言語と手動管理のネイティブコードの間に立つことだが、その境界こそが最悪のバグのほとんどが潜んでいた場所だ。
アナウンスには、v1.3.14 だけで直したものの例が並んでいる。node:zlib の heap-use-after-free、re-entrant なコールバック由来の node:http2 の use-after-free、UDPSocket.sendMany の範囲外書き込み、tls.connect ごとのメモリリーク、CSS パーサの double-free。まだ続くし、どれも技術的な話だ。
Sumner は、これは Zig のせいではないとはっきり言っている。「Zig がなければここまで来られなかったし、それにはいつまでも感謝している」。問題は構造的なものだ。GC 境界をまたいだライフタイム管理は、それを前提に設計していない言語ならどれでも難しく、Zig の答えである「すべての呼び出し箇所での明示的な defer」は、めったに通らないエラー経路では間違えやすい。
彼は C++ も検討した。Bun の約 20% はすでに C++ で、さらに移せばコンストラクタとデストラクタが手に入る。だがそれでもコードレビューで守るスタイルガイドに頼ることになり、メモリ破壊はなくならない。
Rust の売りは違う。safe Rust なら、use-after-free も double-free も「解放し忘れ」もコンパイルエラーになる。borrow checker と Drop が、スタイルガイドの問題を型システムの問題に変えるのだ。
戦略:再設計ではなくトランスパイル
書き直し(rewrite)は悪評が高い。理由はある。535K 行を一から書き直せば、機能追加もバグ修正も一年凍結される。Bun が選んだ形は機械的な移植だった。アーキテクチャも性能目標も機能もテストスイートも同じ —— ただ Rust で。
この取り組みを全体で動かしたのは二つの決断だ。
- 一度にやり切る。段階的ではない。 Sumner が esbuild を Zig に移植した経験から、段階的な書き直しは一時的な足場を残し、役に立つより邪魔になると分かっていた。
- トランスパイルされたように見せる。 狙いは、元の Zig から来たと分かるような Rust を書き、v1.4 のリリース後に慣習的な Rust へ向けてリファクタリングすることだ。
決定的だったのは、Bun のテストスイートが TypeScript で書かれていることだ。つまりランタイムが何語で実装されていても気にしない。だから「すべてのテストが green」を完了の定義にできた。
準備は小さかったが意図的だった。コードを書く前に、Sumner は Claude と約 3 時間かけて Zig のパターンを Rust に対応させた。その対話が PORTING.md になった(後に Hacker News に載る)。二度目のパスでは、コードベース中の全構造体フィールドの正しい Rust ライフタイムを分析し、それを LIFETIMES.tsv に書き出した。どちらの文書も、一行も移植する前に敵対的レビューを通った。
そのまま残ったもの:JavaScriptCore と埋め込み済みの C/C++ ライブラリ。書き直したのはネイティブの糊付け部分と Bun 固有のコードで、JS エンジンではない。
実行:64 Claude、11 日
移植は Claude Code 上で約 50 個の「動的ワークフロー」として、11 日間、休みなく動いた。各ワークフローはループだ。タスクを受け取り、コードを出し、レビューを受け、フィードバックを反映する。
面白いのはレビュー体制だ。実装者一人につき、二人以上の敵対的レビュアー —— 「このコードは間違っている」と仮定し、失敗する理由をすべて見つけろと命じられた別の Claude セッション —— がいた。実装者はレビューせず、レビュアーは実装しない。アナウンスはこれを人間のレビューになぞらえている。作者はマージしたいので、別の人がチェックするのだ。
いくつかの仕組みは抜き出す価値がある。
- まず試し走り。 全 1,448 ファイルに踏み切る前に、3 ファイルを移植した。
- worktree での分割。 当初の実行では Claude 同士が
git stashやgit resetで踏みつけ合っていた。解決策は 4 つの worktree、各 16 Claude、タスクの最中は git も cargo も絶対に実行しないというルールだ。 - コンパイラエラーを作業キューに。
cargo checkが約 16,000 件のエラーをファイルに吐き、crate ごとにまとめた。64 の Claude が少しずつ削り、4 つの worktree で 16 の fix/review/apply ループを回した。 - 循環依存。 Zig は事実上ひとつのコンパイル単位だったが、Rust には約 100 の crate が必要だった。サイクルの解体で、その 16,000 件のエラーの大半が浮き彫りになった。
- 隔離。 TCP ソケットを使い切ったり約 1 万プロセスを fork したりするストレステストは
systemd-runの cgroup 下で実行した。それでもマシンはディスク不足で何度かクラッシュした。
timeline
title Bun の 11 日間の Zig → Rust 移植(2026年5月)
2026-05-03 : 移植ブランチをオープン(PR #30412)
2026-05-04 : 最初の100ファイル草稿バッチ
2026-05-06 : 約16,000件のコンパイラエラー、64 Claudes
2026-05-08 : 最初のCI実行(失敗972ファイル)
2026-05-09 : Linux x64 がグリーンに
2026-05-11 : Windows がグリーン(最後のプラットフォーム)
2026-05-14 : 全6プラットフォームがグリーン、マージ済み
数字で見ると:ピーク時は毎分 1,300 行、一番忙しい時間(5 月 6 日)に 695 コミット、最も忙しい一分間で 58 コミット。すべてのコミットは着地前に二人の敵対的レビュアーを通った。最終的な diff は +1,009,272 行。スキップも削除もされたテストはゼロ。
本番さながらになったのは CI だ。最初の実行から二日で、失敗するテストファイルは 972 から 23 へ減った。あと一日半で Linux が完全に green に。Windows は 5 月 11 日に最後に終わり、全六プラットフォームが green になったのは build #54202、5 月 14 日だ。
Rust が実際に手に入れたもの
掲げた目標は安定性で、ポストは硬い数字でそれを裏付けている。
バグ。 Bun v1.4.0 は、v1.3.14 で再現する 128 のバグを直している。
メモリ。 Drop が呼び出し箇所ごとの defer を置き換えた。ポストの例:同じ 60 モジュールのプロジェクトを一つのプロセスで 2,000 回バンドルする。v1.3.14 ではビルドごとに約 3 MB が永久に漏れ続ける。v1.4.0 ではメモリが横ばいになる。
| ビルド回数 | v1.3.14 | v1.4.0 |
|---|---|---|
| 500 | 1,914 MB | 526 MB |
| 1,000 | 3,506 MB | 586 MB |
| 1,500 | 5,097 MB | 608 MB |
| 2,000 | 6,745 MB | 609 MB |
Zig でこれを直す以前の試みは、Sumner によればマージされなかった。等価な Drop がないために、自信を持てなかったからだ。
バイナリサイズ。 Rust への書き直し単体で 3.8 MB(Windows)、5.5 MB(macOS)、6.8 MB(Linux)を削った —— 主に過剰な Zig の comptime を捨てたおかげだ。さらにリンカの手入れ(同一コードの畳み込み、ICU データの削り込み)で、Linux と Windows で合計約 20% の縮小になった。
| バージョン | プラットフォーム | サイズ |
|---|---|---|
| v1.4.0 | Windows | 76 MB |
| v1.3.14 | Windows | 94 MB |
| v1.4.0 | Linux | 70 MB |
| v1.3.14 | Linux | 88 MB |
速度。 C/C++ と Rust の言語をまたぐ LTO により、コンパイラが言語の境界を越えてインライン展開できる。Bun が測った改善は 2–5%。
Bun.serve: 169.6k → 177.7k req/s (+4.8%)express: 64.5k → 66.6k (+3.2%)next build: 13.62s → 13.03s (+4.5%)tsc -b --force: 0.94s → 0.89s (+4.7%)
Prisma は Compute のパブリックベータを Rust 版の移植に乗せた。Alexey Orlenko の言葉:「メモリリークと、VM が一時停止して再開したあとに回復できないコネクションプールに遭遇した。Rust 版の移植が出てきたとき、同じ失敗モードで試した。完璧に処理できた」
Claude Code v2.1.181(6 月 17 日)以降は Rust 版を使っている。起動は Linux で 10% 速い。Sumner の、ユーザーに見える変化への評価はこれだ。「Boring is good.」(退屈こそが良い)
移植が滑ったところ
535K 行の忠実なトランスパイルはタダではない。ポストには 19 の既知の regression が記録されているが、すべて直っている。教訓的なのは、言語間の小さな意味論の穴だ。
debug_assert!の副作用。 Zig のassertは関数なので、その引数はすべてのビルドで実行される。Rust のdebug_assert!はリリースで消えるマクロだ —— HMR の状態を変更していたinsert_staleの呼び出しが、音もなく実行されなくなった。(#30678)- 奇数長のスライス。 Zig のヘルパーは末尾の奇数バイトを無視していた。
bytemuck::cast_sliceはそれでパニックする。UTF-16 の BOM に奇数バイトが続くBlob.text()がクラッシュし始めた。(#31188) - 境界チェック。 Zig の
ReleaseFastは境界チェックを剥ぐ。Rust は残す。プレースホルダ定数のせいで、ファイル名のインターン上限が 8.4M から 270K に下がり、実際のプロジェクトがそれにぶつかった。(#31503) comptimeの書式文字列。 Zig は書式文字列をコンパイル時に評価するので、色マーカーは引数が代入される前に消えている。Rust にcomptimeはないから、マーカーパーサが OSC 8 ハイパーリンク内のリテラルなバックスラッシュを食ってしまった。(#30693)
二つの言語は見た目が同じでも、そうではないゆえに表面化するバグたちだ。
これの意味
LLM の話を脇に置けば、そこにあるのは本物のエンジニアリングの物語だ。ひとつのプロジェクトが、手動メモリ管理が規模において与えられるものの天井にぶつかり、ひとつのバグの階級を「単に慎ませる」ものから「不可能にする」ツールとして Rust の borrow checker を選んだ。
LLM の話こそが、人々が議論する部分だ。Sumner は率直に、これはコードベース全体を把握した三人のエンジニアに約一年かかったはずで、彼らならやらなかっただろう —— 現実的な代替案は「何もせず、バグを直し続ける」ことだった —— と言う。代わりに、64 の Claude を監督する一人のエンジニアが、11 日で、約 $165K でやってのけた。
Bun の Rust コードの約 4% が unsafe ブロックにある(約 78 万行の全体のうち、約 2.7 万行にわたり約 13,000 個の unsafe キーワード)。そのブロックの 78% は一行だ。マージ以降、セキュリティレビューを 11 回実施し、すべてのパーサを横断する 24/7 のカバレッジガイド付きファジングを追加した —— これまでに 1,000 億回の実行、約 15 件の PR。
私が見張るのは保守性の問いだ。Sumner は Rust が Zig のように読めると言い、その主張のために canMergeSymbols の並べて比較を示している。賭けは、忠実な移植なら port ごとにレビュー可能で、それが一人の人間が 100 万行の diff にサインオフすることを可能にしたというものだ。
彼の結びの言葉は、面白く年を取るものだろう。「One engineer can do a lot more today than a year ago.」(今の一人のエンジニアは、一年前よりずっと多くのことができる)