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 は {}", n), Err(e) => println!("エラー: {}", 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()` などのコンビネーターを使えます。 // それ以外は、この関数は上のものと同じで、次のように読めます: // 両方の値を 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 は {}", n), Err(e) => println!("エラー: {}", e), } } fn main() { // これでも妥当な答えが表示されます。 let twenty = multiply("10", "2"); print(twenty); // 次は、はるかに有用なエラーメッセージを表示します。 let tt = multiply("t", "2"); print(tt); }