コマンドライン引数の受け取り

Beginner

This tutorial is from open-source community. Access the source code

はじめに

コマンドライン引数の受け取りへようこそ。この実験は、Rust Bookの一部です。LabEx で Rust のスキルを練習することができます。

この実験では、「minigrep」という新しいプロジェクトを作成します。このプロジェクトは、2 つのコマンドライン引数を受け取ります。検索する文字列とファイルパスです。

コマンドライン引数の受け取り

いつも通り、cargo new を使って新しいプロジェクトを作成しましょう。このプロジェクトには minigrep と名前を付けます。これは、システムに既にインストールされている可能性のある grep ツールと区別するためです。

$ cargo new minigrep
     Created binary (application) `minigrep` project
$ cd minigrep

最初のタスクは、minigrep に 2 つのコマンドライン引数を受け取らせることです。つまり、ファイルパスと検索する文字列です。つまり、cargo run でプログラムを実行し、2 つのハイフンを使って、次の引数が cargo ではなく、私たちのプログラム用であることを示し、検索する文字列と、検索対象のファイルのパスを指定できるようにしたいと思います。例えば、次のようになります。

cargo run -- searchstring example-filename.txt

今のところ、cargo new で生成されたプログラムは、与えられた引数を処理できません。https://crates.io の既存のライブラリの一部を使えば、コマンドライン引数を受け取るプログラムを書くのに役立ちますが、この概念を学び始めたばかりのあなたにとっては、自力でこの機能を実装しましょう。

引数値の読み取り

minigrep に渡すコマンドライン引数の値を読み取らせるには、Rust の標準ライブラリに用意されている std::env::args 関数が必要です。この関数は、minigrep に渡されたコマンドライン引数のイテレータを返します。第 13 章でイテレータについて十分に解説します。今のところ、イテレータに関する 2 つのことだけ知っておけばよいです。イテレータは一連の値を生成し、イテレータに対して collect メソッドを呼び出すことで、イテレータが生成するすべての要素を含むベクターなどのコレクションに変換できます。

リスト 12-1 のコードでは、minigrep プログラムが渡された任意のコマンドライン引数を読み取り、その値をベクターに収集できるようになっています。

ファイル名:src/main.rs

use std::env;

fn main() {
    let args: Vec<String> = env::args().collect();
    dbg!(args);
}

リスト 12-1: コマンドライン引数をベクターに収集して表示する

まず、use 文で std::env モジュールをスコープ内に持ち込み、その args 関数を使えるようにします。std::env::args 関数は 2 つのモジュールにネストされていることに注意してください。第 7 章で説明したように、必要な関数が複数のモジュールにネストされている場合、私たちは関数ではなく親モジュールをスコープ内に持ち込むことにしています。これにより、std::env の他の関数を簡単に使えるようになります。また、use std::env::args を追加してから、単に args で関数を呼び出すよりも曖昧さが少なくなります。なぜなら、args は簡単に現在のモジュールで定義されている関数と間違えられるからです。

args 関数と無効な Unicode

任意の引数に無効な Unicode が含まれている場合、std::env::args はパニックを起こします。プログラムが無効な Unicode を含む引数を受け付ける必要がある場合は、代わりに std::env::args_os を使ってください。この関数は、String 値ではなく OsString 値を生成するイテレータを返します。ここでは単純さのために std::env::args を使っていますが、OsString 値はプラットフォームごとに異なり、String 値よりも扱いが複雑です。

main の最初の行では、env::args を呼び出し、直ちに collect を使ってイテレータを、イテレータが生成するすべての値を含むベクターに変換します。collect 関数を使って多くの種類のコレクションを作成できるので、args の型を明示的に指定して、文字列のベクターを取得したいことを指定します。Rust では型を明示的に指定する必要がほとんどない場合がありますが、collect はコレクションの種類を推論できないため、よく型を明示的に指定する関数の 1 つです。

最後に、デバッグマクロを使ってベクターを表示します。まず、引数を渡さずにコードを実行してみて、次に 2 つの引数を渡して実行してみましょう。

$ cargo run
--snip--
[src/main.rs:5] args = [
"target/debug/minigrep",
]
$ cargo run -- needle haystack
--snip--
[src/main.rs:5] args = [
"target/debug/minigrep",
"needle",
"haystack",
]

ベクターの最初の値は "target/debug/minigrep" であることに注意してください。これは私たちのバイナリの名前で、C の引数リストの動作と一致しており、プログラムが実行時に呼び出された名前を使用できるようにしています。メッセージに表示したり、コマンドラインエイリアスを使ってプログラムを呼び出した場合のプログラムの動作を変更したりするために、プログラム名にアクセスできることは頻繁に便利です。ただし、この章の目的のためには、これを無視して必要な 2 つの引数のみを保存します。

変数に引数値を保存する

プログラムは現在、コマンドライン引数として指定された値にアクセスできるようになっています。次に、2 つの引数の値を変数に保存して、プログラムの残りの部分でそれらの値を使用できるようにする必要があります。それをリスト 12-2 で行います。

ファイル名:src/main.rs

use std::env;

fn main() {
    let args: Vec<String> = env::args().collect();

    let query = &args[1];
    let file_path = &args[2];

    println!("Searching for {}", query);
    println!("In file {}", file_path);
}

リスト 12-2: 検索クエリ引数とファイルパス引数を保持する変数を作成する

ベクターを表示したときに見たように、プログラム名は args[0] のベクターの最初の値を占めています。したがって、引数はインデックス 1 から始まります。minigrep が取る最初の引数は、検索する文字列です。したがって、最初の引数への参照を query 変数に入れます。2 番目の引数はファイルパスになるので、2 番目の引数への参照を file_path 変数に入れます。

これらの変数の値を一時的に表示して、コードが意図通りに動作していることを確認します。このプログラムを再び testsample.txt の引数で実行してみましょう。

$ cargo run -- test sample.txt
   Compiling minigrep v0.1.0 (file:///projects/minigrep)
    Finished dev [unoptimized + debuginfo] target(s) in 0.0s
     Running `target/debug/minigrep test sample.txt`
Searching for test
In file sample.txt

素晴らしい!プログラムが正常に動作しています!必要な引数の値が正しい変数に保存されています。後で、ユーザーが引数を提供しないなどの特定の潜在的なエラー状況を処理するためのエラーハンドリングを追加します。今は、その状況を無視して、代わりにファイル読み取り機能を追加する作業に取り組みます。

まとめ

おめでとうございます!コマンドライン引数の受け取りの実験を完了しました。LabEx でさらに多くの実験を行って、スキルを向上させることができます。