Cargo Featureフラグの設計:マルチターゲット開発を「負債」にせず「武器」にする黄金律
Rustでの開発において、`Cargo.toml`の`[features]`セクションは単なる「機能のON/OFFスイッチ」ではありません。これは、「コンパイル時にバイナリの依存関係と実行コードを抽象化する、強力なメタプログラミングのインターフェース」です。
多くのエンジニアが「なんとなく」フィーチャーフラグを乱立させ、結果としてコンパイル時間の増大や、テストの網羅性欠如、依存関係の地獄(Dependency Hell)を招いています。本稿では、大規模プロジェクトを完遂させるための「Feature設計の黄金律」を伝授します。
—
1. 黄金律:Feature設計の「排他・包含・階層」を制す
Feature設計の最大の敵は「Featureの爆発」と「意図しない有効化」です。
包含関係の明確化:`default`の役割を再定義せよ
Featureは「足し算」で考えるのが基本です。依存関係を整理する際、`default`には「開発者が最も頻繁に使う最小セット」のみを記述すべきです。
[features]
最小構成(最速のコンパイルを目指す)
default = [“std”]
stdライブラリへの依存(no_std対応の基盤)
std = [“dep:serde”, “dep:log”]
特定のバックエンド(包含関係を明示)
“full”は全ての機能を有効にするメタフラグとして運用する
full = [“postgres”, “redis”, “encryption”]
postgres = [“sqlx/postgres”]
redis = [“dep:redis”]
encryption = [“dep:aes”, “dep:ring”]
排他制御:`compile_error!`によるガード
RustにはFeatureの相互排他をCargoレベルで強制する機能はありません。そのため、`lib.rs`の冒頭でコンパイル時に判定を行うのがプロの流儀です。
[cfg(all(feature = “postgres”, feature = “redis”))]
compile_error!(“PostgresとRedisの同時バックエンドは現時点ではサポートされていません。”);
この記述があるだけで、CIで無意味なエラーを追う時間を大幅に短縮できます。
—
2. チーム開発の生産性を底上げする「設定共有」と神ツール
個人のローカル環境で「Featureフラグを付け忘れてテストが通らない」という事故を防ぐため、開発環境をコードで定義します。
.cargo/config.toml による「開発体験の固定」
チーム全員のコンパイル設定を統一するために、リポジトリルートに `.cargo/config.toml` を置くのは必須です。
.cargo/config.toml
[build]
CIとローカルでプロファイル設定を共有
target-dir = “target”
[alias]
よく使うフラグをコマンド化してミスを防ぐ
チーム固有のテストセットをエイリアスで定義
test-full = “test –all-features”
check-fast = “check –no-default-features”
神プラグイン:`cargo-hakari` と `cargo-expand`
- [cargo-hakari](https://github.com/sunshowers/cargo-hakari): 大規模ワークスペースにおけるFeatureの「重複による再コンパイル」を根絶します。ワークスペース内の全クレートの依存関係を最適化し、ビルド時間を劇的に削減します。
- [cargo-expand](https://github.com/dtolnay/cargo-expand): Featureフラグによって生成されたコードが、最終的にどんなRustコードに展開されるかを確認するための必須ツールです。デバッグが困難なマクロ生成コードもこれで一発で解決します。
—
3. テスト実行戦略:フィーチャーの組み合わせ爆発への対抗策
「`–all-features`さえ叩けばいい」という考えは捨ててください。真のテックリードは、フィーチャーの組み合わせを戦略的にテストします。
CIでの戦略的テスト実行
GitHub Actions等のCI環境では、フィーチャーの組み合わせをマトリックスで分割実行し、直列実行時間を削ります。
.github/workflows/ci.yml の抜粋
strategy:
matrix:
include:
- name: “Default features”
flags: “”
- name: “Full features”
flags: “–all-features”
- name: “No default features (minimal)”
flags: “–no-default-features”
steps:
- run: cargo test ${{ matrix.flags }}
—
4. プロの隠しコマンドとキーボードショートカット
最後に、IDE(VS Code + rust-analyzer)を使いこなすための、プロのTipsを共有します。
- `cargo check –workspace –all-targets`: 全てのFeatureとテストターゲットをチェックする最強のコマンドです。コミット前に必ず実行してください。
- VS Codeの設定: `rust-analyzer.cargo.features` を `all` に設定すると、IDEの補完が全機能に対して効くようになり、Featureフラグによるコード欠落エラーを編集中に検知できます。
- 重要: 設定ファイルの `settings.json` に以下を追加してください。
“rust-analyzer.cargo.features”: “all”,
“rust-analyzer.check.allTargets”: true
—
結びに:設計とは「選択肢」を残すこと
Featureフラグの設計は、プロジェクトの将来に対する「選択肢」を残す行為です。機能を分離しておくことで、将来的なマイクロサービス化や、Wasmへの移植、あるいは軽量なエッジデバイスへのデプロイが容易になります。
「面倒だから」とFeatureを肥大化させる前に、一度立ち止まってください。そのフラグは、あなたのチームの3ヶ月後のコードベースを救うものですか? 規律あるCargo設計こそが、持続可能なRust開発の唯一の道です。
さあ、今すぐ `Cargo.toml` を開き、不要な依存関係の枝を剪定することから始めましょう。エンジニアの時間は、コンパイル待ちをするためにあるのではありません。製品を磨くためにあるのです。