Result

Resultは、リッチなバージョンのOption型で_値の不在_の可能性の代わりに_エラー_の可能性を示します。

つまり、Result<T, E>は以下の2つの結果を持ちます。

  • Ok<T>:要素Tが見つかった場合
  • Err<E>:要素Eとともにエラーが見つかった場合

慣例により、Okが期待される結果であり、Errは期待されない結果です。

Optionと同様、Resultは多くのメソッドを持ちます。例えばunwrap()は、Tもしくはpanicをもたらします。エラーハンドリングでは、ResultOptionで重複するコンビネータが多くあります。

Rustを書いていく中で、parse()メソッドなど、Result型を返すメソッドを目にするでしょう。文字列を他の型にパースすることは必ずしも成功する訳ではないため、Resultを返すことで失敗するケースについてもカバーできるのです。

早速、文字列をparse()した場合の成功例と失敗例を見てみましょう。

fn multiply(first_number_str: &str, second_number_str: &str) -> i32 {
    // `unwrap()`で数字を取り出してみましょう。痛い目を見るでしょうか?
    let first_number = first_number_str.parse::<i32>().unwrap();
    let second_number = second_number_str.parse::<i32>().unwrap();
    first_number * second_number
}

fn main() {
    let twenty = multiply("10", "2");
    println!("double is {}", twenty);

    let tt = multiply("t", "2");
    println!("double is {}", tt);
}

失敗例では、parse()がエラーを返すためunwrap()がパニックします。そして、panicはプログラムを終了させて不快なエラーメッセージを出力します。

エラーメッセージを改善するために、リターン型に対してもっと明確になるべきで、またエラーを明示的に処理することを考えるべきです。

main内で使うResult

Result型は、明示的な指定によりmain関数のリターン型にもなります。一般に、main関数は以下のような形になるでしょう。

fn main() {
    println!("Hello World!");
}

一方mainResultをリターン型とすることも可能です。エラーがmain関数内で発生した時、エラーコードを返し、エラーに関するデバッグ表記を(Debugトレイトを使って)出力します。以下の例ではそのようなシナリオを示し、この先の節でカバーする内容に触れていきます。

use std::num::ParseIntError;

fn main() -> Result<(), ParseIntError> {
    let number_str = "10";
    let number = match number_str.parse::<i32>() {
        Ok(number)  => number,
        Err(e) => return Err(e),
    };
    println!("{}", number);
    Ok(())
}