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); }