rs.nkmk.me

Rustの*_orと*_or_elseの違いと使い分け

Posted: | Tags: Rust, エラー処理

RustのResultおよびOptionには*_or*_or_elseという名前のメソッド、例えばmap_ormap_or_elseunwrap_orunwrap_or_elseなどがある。

*_orは先行評価で*_or_elseは遅延評価。関数呼び出しの結果を渡したい場合は*_or_elseの使用が推奨されている。

なお、便宜上*_or*_or_elseと書いているが、orelseandand_thenも同様。

*_orは先行評価で*_or_elseは遅延評価

公式リファレンスにおいて、*_orでは以下のように説明されている。以下はOption::unwrap_orだが、他も同じ。

Arguments passed to unwrap_or are eagerly evaluated; if you are passing the result of a function call, it is recommended to use unwrap_or_else, which is lazily evaluated. Option::unwrap_or in core::option - Rust

*_orに渡された引数は先行評価され(eagerly evaluated)、*_or_elseでは遅延評価される(lazily evaluated)。関数呼び出しの結果を渡したい場合は*_or_elseの使用が推奨されている。

具体例で確認

具体例として、Optionunwrap_orunwrap_or_elseを使う。

Noneのとき、unwrap_orは指定された値を返し、unwrap_or_elseは指定されたクロージャや関数を実行しその値を返す。

動作を確認するために、文字列Stringを返す関数を定義しておく。いつ呼び出されたか分かるように、引数に指定した数値を出力する。

fn test_func(i: i32) -> String {
    println!("Print from function: No.{i}");
    String::from("N/A")
}

ベクタVecからpopで要素を取り出す。popはベクタが空のときNoneを返す。

要素数1個のベクタだと、1回目のpopSome、2回目のpopNoneを返す。

unwrap_orの場合、1回目も2回目も指定した関数が呼び出される。

let mut v: Vec<String> = vec![String::from("abc")];

assert_eq!(v.pop().unwrap_or(test_func(0)), "abc");
// Print from function: No.0

assert_eq!(v.pop().unwrap_or(test_func(1)), "N/A");
// Print from function: No.1

unwrap_or_elseの場合、2回目(Noneのとき)のみ関数が呼び出される。ここで|| ...は引数なしのクロージャ。

let mut v: Vec<String> = vec![String::from("abc")];

assert_eq!(v.pop().unwrap_or_else(|| test_func(2)), "abc");

assert_eq!(v.pop().unwrap_or_else(|| test_func(3)), "N/A");
// Print from function: No.3

unwrap_orの1回目の関数呼び出しは無駄なので、関数呼び出しの結果を渡したい場合はunwrap_or_elseの方がよい。

なお、Option*_or_elseに指定するクロージャや関数には何も渡されないが、Result*_or_elseに指定するクロージャや関数にはエラーEが渡される。

Result*_or_elseでは、処理に使わなくても引数を一つ受け取るクロージャや関数を指定する必要がある。例えばクロージャの場合|_| ...のようにする。

具体例は以下の記事を参照。

関連カテゴリー

関連記事