はじめに
コマンドライン引数の受け取りへようこそ。この実験は、Rust Bookの一部です。LabExでRustのスキルを練習することができます。
この実験では、「minigrep」という新しいプロジェクトを作成します。このプロジェクトは、2つのコマンドライン引数を受け取ります。検索する文字列とファイルパスです。
This tutorial is from open-source community. Access the source code
💡 このチュートリアルは英語版からAIによって翻訳されています。原文を確認するには、 ここをクリックしてください
コマンドライン引数の受け取りへようこそ。この実験は、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
変数に入れます。
これらの変数の値を一時的に表示して、コードが意図通りに動作していることを確認します。このプログラムを再び test
と sample.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でさらに多くの実験を行って、スキルを向上させることができます。