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