Rustの*_orと*_or_elseの違いと使い分け
RustのResult
およびOption
には*_or
と*_or_else
という名前のメソッド、例えばmap_or
とmap_or_else
、unwrap_or
とunwrap_or_else
などがある。
*_or
は先行評価で*_or_else
は遅延評価。関数呼び出しの結果を渡したい場合は*_or_else
の使用が推奨されている。
なお、便宜上*_or
と*_or_else
と書いているが、or
とelse
やand
とand_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 useunwrap_or_else
, which is lazily evaluated. Option::unwrap_or in core::option - Rust
*_or
に渡された引数は先行評価され(eagerly evaluated)、*_or_else
では遅延評価される(lazily evaluated)。関数呼び出しの結果を渡したい場合は*_or_else
の使用が推奨されている。
具体例で確認
具体例として、Option
のunwrap_or
とunwrap_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回目のpop
はSome
、2回目のpop
はNone
を返す。
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
が渡される。
- Option::unwrap_or_else in std::option - Rust
where F: FnOnce() -> T
- Result::unwrap_or_else in std::result - Rust
where F: FnOnce(E) -> T
Result
の*_or_else
では、処理に使わなくても引数を一つ受け取るクロージャや関数を指定する必要がある。例えばクロージャの場合|_| ...
のようにする。
具体例は以下の記事を参照。