概要
Rust初心者として「crateって何?」とClaude Codeに聞いたところからなんか盛り上がったので記事に。
crateとは何か
Takazudo: RustのcrateってNode.jsでいうとバンドラーがコンパイルしてtree shakeして圧縮した巨大なスクリプトみたいなもの?
Claude Code: いいえ、crateはnpmパッケージに近いもの。対応関係はこう。
| Rust | Node.js equivalent |
|---|---|
| crate | npm package |
| Cargo.toml | package.json |
| Cargo (the tool) | npm/pnpm + bundler combined |
| crates.io | npmjs.com |
crateには2種類ある。
- Binary crate: 実行可能ファイルにコンパイルされる。
src/main.rsがエントリポイント - Library crate: 他のcrateが使うライブラリ。
src/lib.rsがエントリポイント
Cargoが依存解決もコンパイルも一度にやるので「バンドリング」に見えるが、crate自体はパッケージ/ライブラリであって、バンドリングプロセスではない。
# Rust: one tool does it all
cargo build # resolves deps + compiles → single binary
# Node.js: separate steps
pnpm install # resolves deps
pnpm build # bundles/transpiles (webpack, vite, etc.)
CargoはRust世界の唯一の統一コンパイラ?
Takazudo: Node.jsだとnodeコマンドが基本にあって、webpack, rollup, viteとかバンドラーがたくさんあるけど、CargoはRust世界のオールインワン?
Claude Code: はい。Cargoは単一の標準ツール。
| Concern | Node.js (fragmented) | Rust (unified) |
|---|---|---|
| Package management | npm / pnpm / yarn | Cargo |
| Dependency resolution | npm / pnpm / yarn | Cargo |
| Build / compile | webpack / vite / rollup / esbuild | Cargo (calls rustc) |
| Run scripts | node, npm run | Cargo (cargo run) |
| Test runner | jest / vitest / mocha | Cargo (cargo test) |
| Linting | eslint | Cargo (cargo clippy) |
| Formatting | prettier | Cargo (cargo fmt) |
| Publish | npm publish | cargo publish |
補足として、rustcが実際のコンパイラ。Cargoはビルドシステム+パッケージマネージャーとしてrustcをオーケストレーションする。
「Cargo2」は生まれないのか
Takazudo: 「もっと賢くCargoみたいなことができるから、Cargo2作った!」みたいな概念はないの?Node.jsの世界ではバンドラーが永遠に開発され続けてるのに。
Claude Code: いいえ、Rustではそれは起きない。理由は3つある。
1つ目。Cargoは公式。Rust自体に同梱されている。コミュニティツールではない。
2つ目。「バンドラー戦争」の余地がない。コンパイルが言語に組み込まれている。
3つ目。Cargoは拡張可能。置き換えるのではなくサブコマンドで拡張する。
cargo clippy # linter (plugin)
cargo fmt # formatter (plugin)
cargo flamegraph # profiling (plugin)
cargo expand # macro expansion (plugin)
JS界隈の最新ツールはRustに向かっている
Claude Code: 皮肉なことに、最新のJSツールはRustで書かれている。
| Tool | What it replaces | Written in |
|---|---|---|
| SWC | Babel (transpiler) | Rust |
| esbuild | webpack (bundler) | Go |
| Turbopack | webpack | Rust |
| Oxc | ESLint + parser | Rust |
| Biome | ESLint + Prettier | Rust |
| Rolldown | Rollup (used in Vite) | Rust |
JSのツールチェーンはRustのモデルに収束しつつある。
そもそもの原因: 10日で設計された言語
Takazudo: 結局、小さなスクリプトとして設計されたのに、今はサーバーサイドで使ってる!型が必要!みたいに追加が永遠に起きてるだけでは?
Claude Code: まさにその通り。1995年にBrendan Eichが10日で作った言語。
1995 JS誕生 → ただの小さなスクリプト
1999 AJAX → あ、非同期が必要
2006 jQuery → あ、DOM抽象化が必要
2009 Node.js → あ、サーバーサイドが必要
2009 CommonJS → あ、モジュールが必要
2012 TypeScript → あ、型が必要
2015 ES6/ESM → あ、ちゃんとしたモジュールが必要(また)
2015 React/Angular → あ、コンポーネントフレームワークが必要
2016 webpack → あ、バンドリングが必要
2017 async/await → あ、もっと良い非同期が必要(また)
2020 Deno → あ、もっと良いランタイムが必要
2023 RSC → あ、サーバーコンポーネントが必要
2024 Bun → あ、もっと速いランタイムが必要
Rustは2010年以降にこれら全てを知った上で設計された。最初から全部組み込み。
JavaScriptの根本的な悲劇として、置き換えるには成功しすぎているが、今の使い方のために設計されたわけではない。小屋として建てた家が50階建てビルになった。でも基礎は小屋のまま。
じゃあ今スクリプト言語を設計したらRustみたいになる?
Takazudo: あ、じゃあ今ウェブ用のスクリプトを設計したら、Rustみたいになるってこと?
Claude Code: ある程度そうで、実際にそうなりつつある。でもブラウザがJavaScriptを実行する必要がある限り、「ソース → 最適化 → 配信」のステップは常に必要。
TurbopackがNext.js限定、Astroは別路線
Takazudo: でもその問題がわかってても統一された答えはないよね?TurbopackはNext.jsでは速いけど他のプロジェクトでは使われてない。Astroはas-you-likeなアダプター方式。
Claude Code: その通り。JS世界は構造的に統一できない。
Frameworks have different needs:
Next.js → SSR, RSC, edge → Turbopack (Vercel-owned)
Astro → islands, MPA → Vite
Remix → loaders, SSR → Vite
Nuxt → Vue SSR → Vite
SvelteKit → Svelte compilation → Vite
深い問題はビジネスインセンティブにある。
Rust: Mozilla/Rust Foundation → 利益動機なし → みんなのための一つのツール
JS: Vercelが Turbopackを所有 → 競争優位 → 普遍的にはしない
Astroチーム → 異なる哲学 → Viteを選択
ViteがJS版Cargoに最も近いが、それでもtheアンサーにはなれない。
Bunが全てを解決しないのか
Takazudo: でもあなたの話が正しければ、全部Bunに移行できるはずでは?でもそうなってない。
Claude Code: Bunは「JSのCargo」を目指した。でも移行が起きない理由がある。
1つ目。npmに200万以上のパッケージがあり、すべてNode.jsの動作を前提にしている。Bunは「ほぼ互換」だが100%ではない。
2つ目。企業はリスクを取らない。「200の依存関係で動く?」「だいたい」「…Nodeのままで」という流れになる。
3つ目。速度は本当の痛みではない。多くのチームはランタイム速度ではなく、複雑さやツールの断片化がボトルネックになっている。
4つ目。スタートアップの製品であること。資金が尽きたら?Rustはファウンデーションに支えられている。
根本的な教訓として、Rustはゼロから始めたから統一できた。JSには30年のレガシーがある。建物に人が住んでいる状態で基礎は直せない。
感想
んーなるほど。