質問: Rustの“unwrap()”は何をするものですか?を読んでいて、また、自身の短い経験を通して疑問をいただいたことがあるので質問です。 `unwrap()`を使うシーンはいくつかあったのですが、なぜそもそも`Result`のように`unwrap()`しないと値を取り出せない形になっているのでしょうか。そのまま`T`で値を返してくれたほうが毎回`unwrap()`しなくて便利だと思いますがどういった理由が考えられますでしょうか。
回答: 関数の中には処理に失敗する可能性があるものもあります。たとえば、文字列を数字に変換する関数は入力の文字列が `"1"` なら整数の `1` に変換できますが、 `"abc"` では変換できません。また、ファイルを開いたり、ファイルから読み込んだり、書き込んだりする関数も処理に失敗する可能性があります(ファイルが存在しない、ディスクの空きスペースがなくなったなど) 処理が失敗した時には何らかの形で呼び出し元にそのことを伝えないといけません。そうすることで呼び出し元では適切なエラーメッセージを表示したり、なんらかの復旧処理(たとえばファイルが存在しないなら新たに作るなど)を行なったりできます。いくつかの言語では、処理の失敗を「例外」(exception)で表現しますが、Rust では「値」で表現します。 `Result` 型はそのような、処理が失敗するかもしれないことを表す値のひとつです。そして `Result` の `unwrap()` は値が `Ok` の時は `T` を返し、`Err` の時は panic を起こしてスレッドを強制終了します。 実用的なプログラムを開発する際、開発の初期では `unwrap()` を多用しますが、最終的には、処理の失敗を適切に処理するため、ほとんど全ての `unwrap()` を `match` 式やコンビネータメソッドによる処理や、`?` 演算子によるエラーの伝搬に置き換えます。 たとえばこのプログラムは `unwrap()` が二つあり、ユーザーの入力に問題があると panic する可能性があります。 ``` use std::env; fn double_arg(mut argv: env::Args) -> i32 { let arg1 = argv.nth(1).unwrap(); let n = arg1.parse::().unwrap(); 2 * n } fn main() { let n = double_arg(env::args()); println!("{}", n); } ``` これを以下のような感じに書き換えると、ユーザーの入力に問題があったら適切なエラーメッセージを表示して終了するようになります。 ``` use std::env; fn double_arg(mut argv: env::Args) -> Result { let arg1 = argv.nth(1).ok_or(CliError::NotEnoughArgs)?; let n = arg1.parse::()?; Ok(2 * n) } // いろいろと省略 // ... fn main() { match double_arg(env::args()) { Ok(n) => println!("{}", n), Err(err @ CliError::NotEnoughArgs) => println!("エラー:{}", err), Err(CliError::Parse(..)) => { println!("エラー:不正な数字です \"{}\"", env::args().nth(1).unwrap()) } } } ``` 手前味噌ですが、Rust におけるエラー処理の基本をこちらの記事で詳しく説明していますので、お時間のある時にぜひ読んでみてください。 * RustでOption値やResult値を上手に扱う