はじめに
実際の Rust プログラムでは、すべての機能を最初から実装するのではなく、目的に特化したライブラリを再利用することがよくあります。公開されている Rust ライブラリは crate と呼ばれ、パッケージが使用する crate は 依存関係 です。依存関係を安全に選び、追加することは、Rust 開発で一般的な作業です。
この実験では、準備済みの greeting プログラムにコマンドラインパーサー clap を追加します。ソースコードを 2 か所変更し、生成されたヘルプと入力値の検証を確認します。また、Cargo.toml と Cargo.lock がそれぞれどのような役割を持つのかを学びます。最初のビルドを高速に行えるよう、依存関係はセットアップ時にあらかじめダウンロードされています。ただし、学習用プロジェクトに依存関係を追加する作業は自分で行います。
Cargo で依存関係を追加する
このステップでは、準備済みのパッケージに clap を追加し、Cargo がマニフェストに加えた変更を確認します。
プロジェクトは /home/labex/project/hello-cli にあります。Cargo コマンドを実行する前に、このディレクトリへ移動してください。
cd /home/labex/project/hello-cli
nano でパッケージマニフェストを開きます。
nano Cargo.toml
Cargo.toml には、パッケージと直接の依存関係が記述されています。空の [dependencies] テーブルは、このプロジェクトが現在 Rust の標準ライブラリだけを使用していることを示します。ファイルを変更せずに nano を終了するには、Ctrl+X を押してください。
cargo add コマンドを使うと、依存関係のテーブルを安全に更新できます。バージョン 4.6.7 の clap を追加し、derive feature を有効にします。
cargo add clap@4.6.7 --features derive
feature は、crate のオプション機能を有効にします。ここでは derive によって、Rust の struct や enum からコマンドラインパーサーを生成するマクロが有効になります。Cargo は有効な feature を +、無効な feature を - とともに表示します。無効な feature が表示されてもエラーではありません。
もう一度マニフェストを開きます。
nano Cargo.toml
依存関係のテーブルに、次のような行が追加されています。
clap = { version = "4.6.7", features = ["derive"] }
バージョンは互換性の要件です。Cargo は互換性のある 4.x の新しいリリースを選択することがあります。実際に選択された正確なバージョンは、別途 Cargo.lock に記録されます。nano を閉じるには Ctrl+X を押してください。
コマンドラインパーサーを derive する
このステップでは、derive マクロとコマンドメタデータを使って Cli struct を clap に接続します。
準備済みのソースファイルを開きます。
nano src/main.rs
初心者向けコースでは、すでに #[derive(Debug)] を使用しました。derive マクロを使うと、型の構造をもとに、crate に trait の実装を生成させることができます。struct Cli の上にある最初の TODO コメントを、次の 2 行に置き換えてください。
#[derive(Parser)]
#[command(version, about = "Create a friendly greeting")]
#[derive(Parser)] は、パース処理を生成します。#[command(...)] 属性は、コマンド全体に関する情報を指定します。version は Cargo.toml のパッケージバージョンを読み取り、about は短い説明を設定します。
Ctrl+O で保存し、Enter を押してから、Ctrl+X で終了します。プログラムを実行せずに確認します。
cargo check
最初のチェックでは、clap とその依存 crate がコンパイルされるため、複数の Compiling 行や Checking 行が表示されることがあります。Finished で始まる最終行が表示されれば、依存関係と生成されたパーサーが一緒にコンパイルできたことを示します。
次に、プログラムにヘルプを表示させます。-- は Cargo のオプションと、プログラムに渡す引数を区切ります。
cargo run --quiet -- --help
出力には、説明、必須の <NAME> 値、自動生成されたヘルプオプションとバージョンオプションが含まれます。
Create a friendly greeting
Usage: hello-cli <NAME>
...
データの構造を定義するだけで、clap がその構造から一貫したヘルプと引数の検証を生成しました。
オプションの繰り返しフラグを追加する
このステップでは、型付きオプションを追加し、パースされた値を小さなループで使用します。
まず、位置引数として名前を 1 つ指定してコマンドを実行します。
cargo run --quiet -- Ada
Hello, Ada!
name: String には #[arg(...)] 属性がないため、必須の位置引数になります。もう一度ソースファイルを開きます。
nano src/main.rs
Cli の内部にある 2 つ目の TODO コメントを、次の内容に置き換えてください。
/// Number of greetings to print
#[arg(short, long, default_value_t = 1)]
times: u8,
ドキュメントコメントはヘルプテキストになります。short は -t を作成し、long は --times を作成します。default_value_t = 1 は、オプションが指定されなかった場合に型付きのデフォルト値を設定します。フィールドの型が u8 なので、clap は符号なし 8 ビット整数として無効な値も拒否します。
最後の TODO コメントと、1 行の println! を次のコードに置き換えてください。
for _ in 0..cli.times {
println!("Hello, {}!", cli.name);
}
アンダースコアは、ループ回数そのものを意図的に使用しないことを示します。nano で保存して終了し、3 回あいさつを表示します。
cargo run --quiet -- Ada --times 3
Hello, Ada!
Hello, Ada!
Hello, Ada!
無効な値も試してください。
cargo run --quiet -- Ada --times many
このコマンドは失敗するはずです。clap は many が有効な u8 ではないことを説明するエラーを表示し、main が無効な値を使用する前に、ゼロ以外の終了ステータスで終了します。
ロックされた依存関係グラフを確認して再利用する
このステップでは、Cargo が解決した依存関係グラフを確認し、ネットワークに接続しなくてもロックファイルから同じグラフを再現できることを確認します。
clap は 直接の依存関係 ですが、clap 自身も依存している crate があります。解決済みグラフの第 1 階層を表示します。
cargo tree --depth 1
正確なパッチバージョンは、Cargo.toml に記載した互換性要件より新しい場合があります。重要なのは、次のような構造になっていることです。
hello-cli v0.1.0 (...)
└── clap v4...
生成されたロックファイルを開きます。
nano Cargo.lock
Cargo.lock は生成されるデータなので、通常は手作業で編集しません。Cargo が依存関係グラフ全体に対して選択した正確なバージョンとチェックサムを記録します。この CLI のようなアプリケーションでは、チームメンバーや自動ビルドで同じ依存関係の解決結果を再利用できるよう、ロックファイルをプロジェクトと一緒に管理します。nano を閉じるには Ctrl+X を押してください。
次に、既存のロックファイルとローカルの crate キャッシュの両方を必須にして確認します。
cargo check --locked --offline
--locked は Cargo.lock の変更を拒否します。--offline はネットワークアクセスを無効にします。最後に Finished 行が表示されれば、すでにダウンロード済みの依存関係グラフだけを使い、別のバージョンを暗黙に解決することなく、このプロジェクトをチェックできたことが分かります。
まとめ
Cargo を使って直接の crate 依存関係を追加し、オプションの feature を有効にして、型付きの clap パーサーを derive しました。また、自動生成されたヘルプと入力値の検証を確認しました。さらに、Cargo.toml に記述された互換性要件と、Cargo.lock に記録された正確な依存関係グラフを区別し、ロックされたグラフがオフラインでも機能することを確認しました。


