ツールのオールインワン
Rust のツールチェーンが特別な理由
多くのプログラミング言語では,開発に必要なツールが分散している.C/C++ では,ビルドシステム(Make, CMake, Ninja, Meson, ...),パッケージマネージャ(Conan, vcpkg, ...),フォーマッタ(clang-format),リンター(clang-tidy, cppcheck)がそれぞれ独立したプロジェクトとして存在し,プロジェクトごとにどのツールを採用するかが異なる.JavaScript でも,パッケージマネージャだけで npm, yarn, pnpm, bun が乱立し,ビルドツールは webpack, Vite, esbuild, Rollup, Turbopack と選択肢が膨大になる.
Rust はこの問題に対して明確な答えを持っている: Cargo という単一のツールに,開発に必要なほぼすべての機能を統合した.
Cargo: 単なるビルドツールではない
Bitterless Rust では Cargo を「ビルドツール兼パッケージマネージャ」と紹介した.これは正しいが,Cargo の全貌からすると氷山の一角にすぎない.
Cargo が担う役割を列挙すると:
| コマンド | 役割 |
|---|---|
cargo new / cargo init |
プロジェクトの作成 |
cargo build |
ビルド |
cargo run |
ビルド + 実行 |
cargo check |
型チェック(バイナリ生成なし) |
cargo test |
テストの実行 |
cargo bench |
ベンチマークの実行 |
cargo doc |
ドキュメント生成 |
cargo fmt |
コードフォーマット |
cargo clippy |
リント(静的解析) |
cargo add / cargo remove |
依存関係の管理 |
cargo publish |
crates.io へのパッケージ公開 |
cargo install |
バイナリクレートのインストール |
cargo update |
依存関係のアップデート |
cargo tree |
依存関係ツリーの表示 |
cargo fix |
コンパイラの提案する修正の自動適用 |
これらが すべて一つのツールに統合されている.プロジェクトの作成からテスト,ドキュメント生成,パッケージ公開まで,cargo コマンドだけで完結する.
この統一性がもたらす恩恵
統一されたツールチェーンの恩恵は,単に「覚えることが少ない」だけではない.
すべての Rust プロジェクトが同じ構造を持つ. Cargo.toml を見ればプロジェクトの依存関係がわかり,src/ 以下にソースコードがあり,cargo test でテストが動く.GitHub で見つけた任意の Rust プロジェクトに対して,この前提が成り立つ.
これは C/C++ の世界では考えられないことだ.CMake を使うプロジェクト,Meson を使うプロジェクト,Autotools を使うプロジェクト — ビルド方法を理解するだけでも一苦労する.
cargo test: 統合されたテスト
Rust のテストは,外部のテストフレームワークを導入することなく書ける.ソースコード内に直接テストを書く:
fn add(a: i32, b: i32) -> i32 {
a + b
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_add() {
assert_eq!(add(1, 2), 3);
}
#[test]
fn test_add_negative() {
assert_eq!(add(-1, 1), 0);
}
}
#[cfg(test)] は条件付きコンパイル属性で,テスト実行時にのみこのモジュールがコンパイルされることを意味する.#[test] 属性がついた関数がテストケースとして認識される.
cargo test
running 2 tests
test tests::test_add ... ok
test tests::test_add_negative ... ok
test result: ok. 2 passed; 0 failed; 0 ignored
テストが言語とツールチェーンに統合されていることで,すべての Rust ライブラリが同じ方法でテストを実行できる.cargo test だけでよい.
cargo bench: ベンチマーク
パフォーマンス計測も Cargo に統合されている.nightly Rust では標準のベンチマーク機能が使え,stable では criterion クレートを使うのが一般的だ:
// benches/my_benchmark.rs (criterion 使用)
use criterion::{black_box, criterion_group, criterion_main, Criterion};
fn fibonacci(n: u64) -> u64 {
match n {
0 => 0,
1 => 1,
_ => fibonacci(n - 1) + fibonacci(n - 2),
}
}
fn criterion_benchmark(c: &mut Criterion) {
c.bench_function("fib 20", |b| b.iter(|| fibonacci(black_box(20))));
}
criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);
cargo bench
プロジェクトのベンチマークが cargo bench 一発で実行できる.
rustfmt: 統一されたフォーマット
rustfmt は Rust の公式フォーマッタであり,cargo fmt で実行できる:
cargo fmt
重要なのは,rustfmt が 公式 であり,Rust コミュニティにおいて 事実上の標準 になっている点だ.
Go 言語が gofmt でフォーマット戦争を終結させたのと同様に,Rust では rustfmt がコードスタイルの議論を終わらせた.インデントはスペース 4 つ,波括弧の位置はこう,use の並び順はこう — これらはプロジェクトごとに議論する必要がない.
rustfmt.toml によるカスタマイズも可能だが,多くのプロジェクトではデフォルト設定がそのまま使われている.
Clippy: コンパイラを超えるリント
Clippy は Rust の公式リンターで,cargo clippy で実行できる:
cargo clippy
Clippy が特に優れているのは,コンパイラが検出しない問題 を指摘してくれる点だ.Rust のコンパイラは非常に厳格で,型安全性やメモリ安全性に関する多くの問題をコンパイル時に検出するが,Clippy はさらにその上をいく.
Clippy が検出するものの例:
イディオマティックでないコード
// Clippy が警告するコード
if x == true {
// ...
}
// Clippy の提案
if x {
// ...
}
パフォーマンス上の問題
// Clippy が警告: 不要な clone
let s = some_string.clone();
println!("{}", s);
// → clone せず参照を使える場面で clone している
// Clippy が警告: collect してからの len
let count = v.iter().filter(|x| x > 0).collect::<Vec<_>>().len();
// → .count() を使うべき
let count = v.iter().filter(|x| x > 0).count();
潜在的なバグ
// Clippy が警告: 浮動小数点の等値比較
if x == 0.0 {
// ...
}
// Clippy の提案: epsilon を使った比較
if x.abs() < f64::EPSILON {
// ...
}
Clippy のリントは 2025 年時点で 700 以上存在し,以下のカテゴリに分類されている:
| カテゴリ | 説明 |
|---|---|
clippy::correctness |
ほぼ確実にバグであるコード |
clippy::suspicious |
怪しいがバグとは限らないコード |
clippy::style |
イディオマティックでないコード |
clippy::complexity |
不必要に複雑なコード |
clippy::perf |
パフォーマンスが改善できるコード |
clippy::pedantic |
より厳格なリント |
clippy::nursery |
実験的なリント |
CI に cargo clippy -- -D warnings を入れることで,Clippy の警告をエラーとして扱い,コード品質を継続的に保つことができる.
cargo doc: ドキュメント生成
ドキュメント生成についてはドキュメンテーションの章で詳しく扱うが,ここでは「Cargo に統合されている」という点だけ触れておく.
cargo doc --open
このコマンドで,プロジェクト内のすべての公開 API のドキュメントが HTML として生成され,ブラウザで開かれる.依存クレートのドキュメントも一緒に生成されるため,オフラインでもすべてのドキュメントを参照できる.
rustup: ツールチェーンのバージョン管理
Cargo の外側には rustup がある.これは Rust ツールチェーン自体のバージョンマネージャだ:
# stable/beta/nightly の切り替え
rustup default stable
rustup default nightly
# クロスコンパイル用ターゲットの追加
rustup target add wasm32-unknown-unknown
rustup target add aarch64-unknown-linux-gnu
# コンポーネントの追加
rustup component add clippy
rustup component add rustfmt
rustup component add rust-analyzer
rustup が管理するのは:
- Rust コンパイラ (rustc) のバージョン
- Cargo のバージョン
- 標準ライブラリ のバージョン
- ターゲットプラットフォーム のサポート
- コンポーネント (clippy, rustfmt, rust-analyzer, etc.)
Node.js でいう nvm と npm を統合して,さらにクロスコンパイル用のツールチェーンまで管理できるようにしたものと考えるとわかりやすい.
rust-analyzer: IDE サポート
rust-analyzer は Rust の公式 Language Server Protocol (LSP) 実装であり,rustup component add rust-analyzer でインストールできる.
LSP サーバーが公式に提供されていることで,VS Code, Neovim, Emacs, IntelliJ — どのエディタを使っていても,同等の補完,型表示,リファクタリング支援が受けられる.
まとめ: 「一つのやり方」の価値
Rust のツールチェーンの素晴らしさは,個々のツールの品質だけでなく,エコシステム全体が統一された方法で動作する という点にある.
任意の Rust プロジェクトに対して:
cargo buildでビルドできるcargo testでテストを実行できるcargo clippyでリントをかけられるcargo fmtでフォーマットできるcargo docでドキュメントを生成できる
この統一性は,個人の開発体験だけでなく,チーム開発や OSS の保守においても絶大な効果を発揮する.新しいプロジェクトに参加したとき,ビルドシステムの使い方を学ぶ必要がない.CI の設定が予測可能になる.コードレビューでスタイルの議論が不要になる.
これは Rust の設計思想 — 正しいやり方を一つ提供し,それをデフォルトにする — の最も成功した実践例の一つだ.