Optionunwrap

以前の例では、甘いレモネードを飲んだ際にpanicを呼び出すことによって、自由にプログラムの実行を失敗させられることが分かりました。では、何らかの飲み物を期待しているにもかかわらず、何も受け取らなかったらどうなるでしょう?これは悲惨なケースになるので、エラーハンドリングする必要があります!

このケースに対して、レモネードと同じように、空文字列("")と比較することもできますが、せっかくRustを使っているので、その代わりにコンパイラに飲み物がないケースを指摘させてみましょう。

stdライブラリの中の、Option<T>と呼ばれるenumは、任意の型Tである変数の値が存在しない可能性がある場合に用いられます。値の状態によって、下記2つのパターンのうちの1つとして扱われます。

  • Some(T): 型Tの値がある場合
  • None: 値が存在しない場合。

これらはmatchを用いて明示的に扱うこともできますし、unwrapで暗黙に処理することもできます。後者はSomeの中の値を返すかpanicするかのどちらかです。

expectメソッドを用いて、panicを手動でカスタマイズできることに触れておきましょう。これは(unwrapをそのまま用いた場合よりも)内容が理解しやすいエラーメッセージを出力するのに役立ちます。次の例では、結果をより明示的に、可能ならいつでもpanicできるように扱っていきます。

// The adult has seen it all, and can handle any drink well.
// All drinks are handled explicitly using `match`.
// 大人は経験豊富なので、大体どんな飲み物にも対処できます。
// あらゆる飲み物は`match`を用いて手動で処理されます。
fn give_adult(drink: Option<&str>) {
    // Specify a course of action for each case.
    match drink {
        Some("lemonade") => println!("Yuck! Too sugary."),
        Some(inner)   => println!("{}? How nice.", inner),
        None          => println!("No drink? Oh well."),
    }
}

// Others will `panic` before drinking sugary drinks.
// All drinks are handled implicitly using `unwrap`.
// 他の人たちは甘い飲み物を飲む前に`panic`します。
// 全ての飲み物は`unwrap`を使って暗黙的に処理されます。
fn drink(drink: Option<&str>) {
    // `unwrap` returns a `panic` when it receives a `None`.
    // `unwrap`を使用すると値が`None`だった際に`panic`を返します。
    let inside = drink.unwrap();
    if inside == "lemonade" { panic!("AAAaaaaa!!!!"); }

    println!("I love {}s!!!!!", inside);
}

fn main() {
    let water  = Some("water");
    let lemonade = Some("lemonade");
    let void  = None;

    give_adult(water);
    give_adult(lemonade);
    give_adult(void);

    let coffee = Some("coffee");
    let nothing = None;

    drink(coffee);
    drink(nothing);
}