Resultのmap
前の例で見たmultiplyでのパニックは、コードを強固にするためには書きません。一般に、呼び出した側がエラーをどのように対処するべきかを自由に決められるように、エラーを呼び出した場所に返すのが好ましいです。
まずは、どのようなエラー型を扱っているのかを知る必要があります。Err型を定めるために、i32に対しFromStrトレイトを使って実装されたparse()を見てみましょう。結果、Err型はParseIntErrorというものであることが分かります。
以下の例では、単純なmatch文が全体として扱いづらいコードにしています。
use std::num::ParseIntError; // リターン型を再度記述することで、パターンマッチングを`unwrap()`なしで行います。 fn multiply(first_number_str: &str, second_number_str: &str) -> Result<i32, ParseIntError> { match first_number_str.parse::<i32>() { Ok(first_number) => { match second_number_str.parse::<i32>() { Ok(second_number) => { Ok(first_number * second_number) }, Err(e) => Err(e), } }, Err(e) => Err(e), } } fn print(result: Result<i32, ParseIntError>) { match result { Ok(n) => println!("n is {}", n), Err(e) => println!("Error: {}", e), } } fn main() { // ここは以前と変わらず、妥当な解を与えます。 let twenty = multiply("10", "2"); print(twenty); // こちらは今度は有益なエラーメッセージを与えます。 let tt = multiply("t", "2"); print(tt); }
幸運にも、Optionのmap、and_then、その他多くのコンビネータもResultのために実装されています。Resultに全てのリストが記載されています。
use std::num::ParseIntError; // `Option`と同様、`map()`などのコンビネータを使うことができます。 // この関数は`map()`を使っている点以外は上記の関数と同じで、 // 両方の値がstrからパース可能であればそれらを乗算し、無効であればエラーをそのまま見送ります。 fn multiply(first_number_str: &str, second_number_str: &str) -> Result<i32, ParseIntError> { first_number_str.parse::<i32>().and_then(|first_number| { second_number_str.parse::<i32>().map(|second_number| first_number * second_number) }) } fn print(result: Result<i32, ParseIntError>) { match result { Ok(n) => println!("n is {}", n), Err(e) => println!("Error: {}", e), } } fn main() { // ここは以前と変わらず、妥当な解を与えます。 let twenty = multiply("10", "2"); print(twenty); // こちらは今度は有益なエラーメッセージを与えます。 let tt = multiply("t", "2"); print(tt); }