-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* add-chapter-on-defaults * add-subtutles
- Loading branch information
1 parent
900a0a6
commit efb11b8
Showing
2 changed files
with
124 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
# Unpacking options and defaults | ||
|
||
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: | ||
* 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(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(Debug)] | ||
enum Fruit { Apple, Orange, Banana, Kiwi, Lemon } | ||
fn main() { | ||
let apple = Some(Fruit::Apple); | ||
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) | ||
} | ||
``` | ||
|
||
## `get_or_insert()` evaluates eagerly, modifies empty value im place | ||
|
||
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: | ||
|
||
``` | ||
#[derive(Debug)] | ||
enum Fruit { Apple, Orange, Banana, Kiwi, Lemon } | ||
fn main() { | ||
let mut my_fruit: Option<Fruit> = None; | ||
let apple = Fruit::Apple; | ||
let first_available_fruit = my_fruit.get_or_insert(apple); | ||
println!("my_fruit is: {:?}", first_available_fruit); | ||
println!("first_available_fruit is: {:?}", first_available_fruit); | ||
// my_fruit is: Apple | ||
// first_available_fruit is: Apple | ||
//println!("Variable named `apple` is moved: {:?}", apple); | ||
// TODO: uncomment the line above to see the compliler error | ||
} | ||
``` | ||
|
||
## `get_or_insert_with()` evaluates lazily, modifies empty value im place | ||
|
||
Instead of explicitly providing a value to fall back on, we can pass a closure to `get_or_insert_with`, as follows: | ||
``` | ||
#[derive(Debug)] | ||
enum Fruit { Apple, Orange, Banana, Kiwi, Lemon } | ||
fn main() { | ||
let mut my_fruit: Option<Fruit> = None; | ||
let get_lemon_as_fallback = || { | ||
println!("Providing lemon as fallback"); | ||
Fruit::Lemon | ||
}; | ||
let first_available_fruit = my_fruit | ||
.get_or_insert_with(get_lemon_as_fallback); | ||
println!("my_fruit is: {:?}", first_available_fruit); | ||
println!("first_available_fruit is: {:?}", first_available_fruit); | ||
// Providing lemon as fallback | ||
// my_fruit is: Lemon | ||
// first_available_fruit is: Lemon | ||
// If the Option has a value, it is left unchanged, and the closure is not invoked | ||
let mut my_apple = Some(Fruit::Apple); | ||
let should_be_apple = my_apple.get_or_insert_with(get_lemon_as_fallback); | ||
println!("should_be_apple is: {:?}", should_be_apple); | ||
println!("my_apple is unchanged: {:?}", my_apple); | ||
// The output is a follows. Note that the closure `get_lemon_as_fallback` is not invoked | ||
// should_be_apple is: Apple | ||
// my_apple is unchanged: Some(Apple) | ||
} | ||
``` | ||
|
||
### See also: | ||
|
||
[`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] | ||
|
||
[closures]: https://doc.rust-lang.org/book/ch13-01-closures.html | ||
[get_or_insert]: https://doc.rust-lang.org/core/option/enum.Option.html#method.get_or_insert | ||
[get_or_insert_with]: https://doc.rust-lang.org/core/option/enum.Option.html#method.get_or_insert_with | ||
[moved]: https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html | ||
[or]: https://doc.rust-lang.org/core/option/enum.Option.html#method.or | ||
[or_else]: https://doc.rust-lang.org/core/option/enum.Option.html#method.or_else |