]> git.proxmox.com Git - rustc.git/blob - src/doc/rust-by-example/src/error/option_unwrap/defaults.md
New upstream version 1.68.2+dfsg1
[rustc.git] / src / doc / rust-by-example / src / error / option_unwrap / defaults.md
1 # Unpacking options and defaults
2
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?
6
7 ## `or()` is chainable, evaluates eagerly, keeps empty value intact
8
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.
10
11 ```rust,editable
12 #[derive(Debug)]
13 enum Fruit { Apple, Orange, Banana, Kiwi, Lemon }
14
15 fn main() {
16 let apple = Some(Fruit::Apple);
17 let orange = Some(Fruit::Orange);
18 let no_fruit: Option<Fruit> = None;
19
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)
23
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
29 }
30 ```
31
32 ## `or_else()` is chainable, evaluates lazily, keeps empty value intact
33
34 Another alternative is to use `or_else`, which is also chainable, and evaluates lazily, as is shown in the following example:
35
36 ```rust,editable
37 #[derive(Debug)]
38 enum Fruit { Apple, Orange, Banana, Kiwi, Lemon }
39
40 fn main() {
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");
45 Some(Fruit::Kiwi)
46 };
47 let get_lemon_as_fallback = || {
48 println!("Providing lemon as fallback");
49 Some(Fruit::Lemon)
50 };
51
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)
58 }
59 ```
60
61 ## `get_or_insert()` evaluates eagerly, modifies empty value in place
62
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:
64
65 ```rust,editable
66 #[derive(Debug)]
67 enum Fruit { Apple, Orange, Banana, Kiwi, Lemon }
68
69 fn main() {
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
79 }
80 ```
81
82 ## `get_or_insert_with()` evaluates lazily, modifies empty value in place
83
84 Instead of explicitly providing a value to fall back on, we can pass a closure to `get_or_insert_with`, as follows:
85 ```rust,editable
86 #[derive(Debug)]
87 enum Fruit { Apple, Orange, Banana, Kiwi, Lemon }
88
89 fn main() {
90 let mut my_fruit: Option<Fruit> = None;
91 let get_lemon_as_fallback = || {
92 println!("Providing lemon as fallback");
93 Fruit::Lemon
94 };
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)
102
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)
111 }
112 ```
113
114 ### See also:
115
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]
117
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