Skip to content

Commit efb11b8

Browse files
authored
add-chapter-on-defaults (#1580)
* add-chapter-on-defaults * add-subtutles
1 parent 900a0a6 commit efb11b8

File tree

2 files changed

+124
-0
lines changed

2 files changed

+124
-0
lines changed

Diff for: src/SUMMARY.md

+1
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,7 @@
160160
- [Unpacking options with `?`](error/option_unwrap/question_mark.md)
161161
- [Combinators: `map`](error/option_unwrap/map.md)
162162
- [Combinators: `and_then`](error/option_unwrap/and_then.md)
163+
- [Defaults: `or`, `or_else`, `get_or_insert`, 'get_or_insert_with`](error/option_unwrap/defaults.md)
163164
- [`Result`](error/result.md)
164165
- [`map` for `Result`](error/result/result_map.md)
165166
- [aliases for `Result`](error/result/result_alias.md)

Diff for: src/error/option_unwrap/defaults.md

+123
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
# Unpacking options and defaults
2+
3+
The 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+
```
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+
```
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 im 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 evaluaes its parameter, so variable `apple` is moved:
64+
65+
```
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!("my_fruit is: {:?}", first_available_fruit);
74+
println!("first_available_fruit is: {:?}", first_available_fruit);
75+
// my_fruit is: Apple
76+
// first_available_fruit is: Apple
77+
//println!("Variable named `apple` is moved: {:?}", apple);
78+
// TODO: uncomment the line above to see the compliler error
79+
}
80+
```
81+
82+
## `get_or_insert_with()` evaluates lazily, modifies empty value im 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+
```
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!("my_fruit is: {:?}", first_available_fruit);
98+
println!("first_available_fruit is: {:?}", first_available_fruit);
99+
// Providing lemon as fallback
100+
// my_fruit is: Lemon
101+
// first_available_fruit is: 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

0 commit comments

Comments
 (0)