1 # Unpacking options and defaults
3 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:
4 * do we need eager or lazy evaluation?
5 * do we need to keep the original empty value intact, or modify it in place?
7 ## `or()` is chainable, evaluates eagerly, keeps empty value intact
9 `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.
13 enum Fruit { Apple, Orange, Banana, Kiwi, Lemon }
16 let apple = Some(Fruit::Apple);
17 let orange = Some(Fruit::Orange);
18 let no_fruit: Option<Fruit> = None;
20 let first_available_fruit = no_fruit.or(orange).or(apple);
21 println!("first_available_fruit: {:?}", first_available_fruit);
22 // first_available_fruit: Some(Orange)
24 // `or` moves its argument.
25 // In the example above, `or(orange)` returned a `Some`, so `or(apple)` was not invoked.
26 // But the variable named `apple` has been moved regardless, and cannot be used anymore.
27 // println!("Variable apple was moved, so this line won't compile: {:?}", apple);
28 // TODO: uncomment the line above to see the compiler error
32 ## `or_else()` is chainable, evaluates lazily, keeps empty value intact
34 Another alternative is to use `or_else`, which is also chainable, and evaluates lazily, as is shown in the following example:
38 enum Fruit { Apple, Orange, Banana, Kiwi, Lemon }
41 let apple = Some(Fruit::Apple);
42 let no_fruit: Option<Fruit> = None;
43 let get_kiwi_as_fallback = || {
44 println!("Providing kiwi as fallback");
47 let get_lemon_as_fallback = || {
48 println!("Providing lemon as fallback");
52 let first_available_fruit = no_fruit
53 .or_else(get_kiwi_as_fallback)
54 .or_else(get_lemon_as_fallback);
55 println!("first_available_fruit: {:?}", first_available_fruit);
56 // Providing kiwi as fallback
57 // first_available_fruit: Some(Kiwi)
61 ## `get_or_insert()` evaluates eagerly, modifies empty value in place
63 To make sure that an `Option` contains a value, we can use `get_or_insert` to modify it in place with a fallback value, as is shown in the following example. Note that `get_or_insert` eagerly evaluates its parameter, so variable `apple` is moved:
67 enum Fruit { Apple, Orange, Banana, Kiwi, Lemon }
70 let mut my_fruit: Option<Fruit> = None;
71 let apple = Fruit::Apple;
72 let first_available_fruit = my_fruit.get_or_insert(apple);
73 println!("first_available_fruit is: {:?}", first_available_fruit);
74 println!("my_fruit is: {:?}", my_fruit);
75 // first_available_fruit is: Apple
76 // my_fruit is: Some(Apple)
77 //println!("Variable named `apple` is moved: {:?}", apple);
78 // TODO: uncomment the line above to see the compiler error
82 ## `get_or_insert_with()` evaluates lazily, modifies empty value in place
84 Instead of explicitly providing a value to fall back on, we can pass a closure to `get_or_insert_with`, as follows:
87 enum Fruit { Apple, Orange, Banana, Kiwi, Lemon }
90 let mut my_fruit: Option<Fruit> = None;
91 let get_lemon_as_fallback = || {
92 println!("Providing lemon as fallback");
95 let first_available_fruit = my_fruit
96 .get_or_insert_with(get_lemon_as_fallback);
97 println!("first_available_fruit is: {:?}", first_available_fruit);
98 println!("my_fruit is: {:?}", my_fruit);
99 // Providing lemon as fallback
100 // first_available_fruit is: Lemon
101 // my_fruit is: Some(Lemon)
103 // If the Option has a value, it is left unchanged, and the closure is not invoked
104 let mut my_apple = Some(Fruit::Apple);
105 let should_be_apple = my_apple.get_or_insert_with(get_lemon_as_fallback);
106 println!("should_be_apple is: {:?}", should_be_apple);
107 println!("my_apple is unchanged: {:?}", my_apple);
108 // The output is a follows. Note that the closure `get_lemon_as_fallback` is not invoked
109 // should_be_apple is: Apple
110 // my_apple is unchanged: Some(Apple)
116 [`closures`][closures], [`get_or_insert`][get_or_insert], [`get_or_insert_with`][get_or_insert_with], ,[`moved variables`][moved], [`or`][or], [`or_else`][or_else]
118 [closures]: https://doc.rust-lang.org/book/ch13-01-closures.html
119 [get_or_insert]: https://doc.rust-lang.org/core/option/enum.Option.html#method.get_or_insert
120 [get_or_insert_with]: https://doc.rust-lang.org/core/option/enum.Option.html#method.get_or_insert_with
121 [moved]: https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html
122 [or]: https://doc.rust-lang.org/core/option/enum.Option.html#method.or
123 [or_else]: https://doc.rust-lang.org/core/option/enum.Option.html#method.or_else