Unpacking options and defaults
There is more than one way to unpack an Option and fall back on a default if it is None. To choose the one that meets our needs, we need to consider the following:
- do we need eager or lazy evaluation?
- do we need to keep the original empty value intact, or modify it in place?
or() is chainable, evaluates eagerly, keeps empty value intact
or()is chainable and eagerly evaluates its argument, as is shown in the following example. Note that because or's arguments are evaluated eagerly, the variable passed to or is moved.
#[derive(Copy, Drop, Debug)]
enum Fruit {
Apple,
Orange,
Banana,
Kiwi,
Lemon,
}
fn main() {
let apple = Some(Fruit::Apple);
let orange = Some(Fruit::Orange);
let no_fruit: Option<Fruit> = None;
let first_available_fruit = no_fruit.or(orange).or(apple);
println!("first_available_fruit: {:?}", first_available_fruit);
// first_available_fruit: Some(Orange)
// `or` moves its argument.
// In the example above, `or(orange)` returned a `Some`, so `or(apple)` was not invoked.
// But the variable named `apple` has been moved regardless, and cannot be used anymore.
// println!("Variable apple was moved, so this line won't compile: {:?}", apple);
// TODO: uncomment the line above to see the compiler error
}
or_else() is chainable, evaluates lazily, keeps empty value intact
Another alternative is to use or_else, which is also chainable, and evaluates lazily, as is shown in the following example:
#[derive(Copy, Drop, Debug)]
enum Fruit {
Apple,
Orange,
Banana,
Kiwi,
Lemon,
}
fn main() {
let no_fruit: Option<Fruit> = None;
let get_kiwi_as_fallback = || {
println!("Providing kiwi as fallback");
Some(Fruit::Kiwi)
};
let get_lemon_as_fallback = || {
println!("Providing lemon as fallback");
Some(Fruit::Lemon)
};
let first_available_fruit = no_fruit
.or_else( || get_kiwi_as_fallback())
.or_else( || get_lemon_as_fallback());
println!("first_available_fruit: {:?}", first_available_fruit);
// Providing kiwi as fallback
// first_available_fruit: Some(Kiwi)
}