Roc が30万行のコンパイラを Rust から Zig へ書き換え — 「言語選択」の現場判断を徹底解説
はじめに
「Rust から Zig へ書き換える」——この一文だけで身構えるエンジニアは多いだろう。メモリ安全性を武器に普及した Rust を、あえて 1.0 にも達していない Zig に置き換える。しかもプロダクトは 30万行規模のコンパイラだ。2026年7月、関数型言語 Roc の作者 Richard Feldman(elm コミュニティでも著名な rtfeldman)が公開した「How Our Rust-to-Zig Rewrite Is Going」は、Hacker News で 269pt を集める話題となった。本記事では、この書き換えが「Rust より Zig が優れている」という単純な話ではないことを整理し、言語選択という判断が実際どういう変数で決まるのかを、実測値とともに深掘りする。
何が起きたか — 487日かけた全書き換え
Roc チームは、約 30万行の Rust 製コンパイラを Zig へ全面的に書き換え、487日かけて元のコンパイラと同等の機能(feature parity)に到達した。単なる部分的なリファクタリングではなく、パーサからコード生成までを含む全書き換えである。成果物として、Roc 製の小さなゲーム「Rocci Bird」(Roc コード 1,000行)を 31KB の WebAssembly バイナリへコンパイルできる段階まで来ており、最初の番号付きリリース v0.1.0 は2026年後半を目標としている(現時点で nightly ビルドは利用可能)。
興味深いのは、書き換えの直接の動機が「Rust が嫌になったから」ではない点だ。Feldman によれば、彼らのロードマップ上、型推論を唯一の例外として、コンパイラのほぼ全パートを別々の理由でいずれ書き直す予定だった。パーサはエラー耐性を高めるため recursive descent へ作り直したい、といった具合に、部品ごとの再設計が積み重なっていた。「どうせ全部書き直すなら、Ship of Theseus(部品を1つずつ交換)ではなく、いっそ言語ごと見直すのが選択肢になった」——これが出発点である。つまり書き換えの本質は言語批判ではなく、大規模再設計のタイミングで言語という変数も俎上に載せたという現場判断だ。
なぜ Zig だったか — コンパイラ特有の4つの事情
Zig が選ばれた理由は、汎用的な優劣ではなく「コンパイラを書く」という文脈に強く依存している。
1. ビルド時間。 Rust の遅いコンパイルは開発のフィードバックループを直接殺す。実測では Zig の -fincremental によるインクリメンタルリビルドが 35ミリ秒。対する Rust 1.85.0 は 10秒、最新の 1.97.0 でも 3.4秒だ。ざっと 100倍の差がフィードバックの速さに乗る。
2. アロケータ制御。 コンパイラは arena(一括確保・一括解放)や struct-of-arrays といった多様なメモリ戦略を多用する。Rust のエコシステムは単一のグローバルアロケータを前提とする設計が多く、こうした細粒度の制御と相性が悪い。Zig はエコシステム全体がアロケータの明示的な受け渡しを前提に作られており、まさに欲しかった粒度が最初から手に入る。
3. unsafe の宿命。 標準ライブラリは生成した機械語と直接やり取りするため、Rust でも unsafe 注釈が至る所に必要になる。この用途では Rust 最大の売りである借用チェッカがそもそも効かない。実際 Roc コンパイラの unsafe 使用箇所は約1,200——rustc 自身の約4万と比べれば少ないが、「安全性の保証」という前提が崩れる領域では、Rust の優位性は目減りする。
4. 再利用できる資産。 Feldman は皮肉を込めてこう書く。「このプロジェクトで最も豊かな再利用コードの金脈は、あなたがまだ聞いたことのない言語で書かれたオープンソースコンパイラだ」。つまり Zig コンパイラ自身のコードだ。中でも LLVM の破壊的な API 変更から切り離せる安定した LLVM ビットコードシリアライザが得られる点は大きい。Rust では狙った形式の LLVM ビットコードを吐かせるのに苦労していた。
トレードオフを直視する — Zig の弱点と、安全性の実測
ここで記事が誠実なのは、Zig の欠点を並べている点だ。private なフィールドが無く内部状態へのアクセスをコンパイル時に防げない、命名規約が snake_case と camelCase で不統一、後方互換性が無くアップグレードのたびにコード修正が要る、デッドコード検出が Rust より弱い——これらは実運用で効いてくる摩擦だ。一方で「データレイアウトの制御が好きだ」と語り、u7 や u5 といった2の冪でない整数型や packed struct、そしてパースを介さずディスクから memcpy 相当で構造体を復元できるゼロパース・デシリアライズを利点に挙げる。マクロは無く、問題は comptime(コンパイル時実行)と普通の関数で解く思想だ。
最も重要なのはメモリ安全性の実測比較である。両バージョンのバグ報告を突き合わせた結果、Rust版のメモリ破壊バグは21件(大半はミスコンパイルで、unsafe コード起因の失敗ではない)、Zig版は10件(8件がミスコンパイル、2件がエラー報告処理での use-after-free)だった。結論は「実務上、意味のある差は無かった(no appreciable difference)」。Zig の use-after-free 2件は確かに Rust の借用チェッカなら捕まえられたが、全体から見れば些少だった。この一節は、Rust 推進派・Zig 推進派どちらの単純な物語にも与しない、貴重なデータポイントだ。ただし注意すべきは、これはコンパイラという特殊なドメインでの話であり、unsafe が少ない一般的なアプリケーションで同じ結論になる保証はない。
エンジニアへの影響 — 「言語選択」から何を学ぶか
この事例から実務に持ち帰れる教訓は、「Zig に乗り換えよう」ではない。むしろ言語選択は絶対的な優劣ではなく、ドメインとの適合で決まるという一点だ。借用チェッカという Rust の中核価値は、unsafe が支配的なコンパイラ標準ライブラリでは効かなかった。逆に言えば、Web バックエンドやCLIツールのように借用チェッカが素直に効く領域では、この書き換えの論理はそのまま Rust を捨てる理由にはならない。
実務での判断軸として抽出できるのは次の3点だ。第一に、ビルド時間はフィードバックループを通じて生産性に直結する——100倍差は無視できるコストではない。第二に、**エコシステムの前提(グローバルアロケータ vs 明示的アロケータ)**が自分の設計と噛み合うかを見る。第三に、その言語の最大の売りが自分のユースケースで本当に効くかを疑う。Rust の安全性、Go の並行性、Zig の制御性——看板の機能が自分の文脈で発揮されなければ、それは選定理由にならない。なお Zig は依然 pre-1.0 であり後方互換性が保証されない点は、採用判断で重く見るべきリスクだ。
まとめ
- Roc チームは30万行の Rust 製コンパイラを Zig へ487日かけて全書き換えし、機能同等に到達した。
- 動機は Rust 批判ではなく「どうせ全書き換えするなら言語も見直す」という再設計のタイミング判断。
- 決め手はコンパイラ特有の事情(インクリメンタルビルド100倍、細粒度アロケータ制御、
unsafe前提で借用チェッカが効かない、Zig資産の再利用)。 - メモリ安全性の実測差は「意味のある差は無かった」——ただしこれは
unsafeの多いコンパイラでの結論。 - 教訓は「言語に絶対の優劣は無く、ドメイン適合で決まる」こと。看板機能が自分の文脈で効くかを疑うのが、言語選択の本質だ。
Zig の 1.0 は未達で後方互換性のリスクは残るが、「コンパイラを書くなら Zig」という選択肢が実データとともに提示された意味は大きい。言語選定に迷うすべてのエンジニアにとって、優劣ではなく適合で考える良質な事例研究である。