mirror of
https://github.com/rust-lang/book.git
synced 2026-05-17 12:03:53 -04:00
Merge pull request #3702 from veera-sivarajan/add-letElse
Introduce `let`-`else` statement
This commit is contained in:
6
listings/ch06-enums-and-pattern-matching/listing-06-07/Cargo.lock
generated
Normal file
6
listings/ch06-enums-and-pattern-matching/listing-06-07/Cargo.lock
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
[[package]]
|
||||
name = "enums"
|
||||
version = "0.1.0"
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
[package]
|
||||
name = "enums"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
@@ -0,0 +1,45 @@
|
||||
#[derive(Debug)] // so we can inspect the state in a minute
|
||||
enum UsState {
|
||||
Alabama,
|
||||
Alaska,
|
||||
// --snip--
|
||||
}
|
||||
|
||||
// ANCHOR: state
|
||||
impl UsState {
|
||||
fn existed_in(&self, year: u16) -> bool {
|
||||
match self {
|
||||
UsState::Alabama => year >= 1819,
|
||||
UsState::Alaska => year >= 1959,
|
||||
// -- snip --
|
||||
}
|
||||
}
|
||||
}
|
||||
// ANCHOR_END: state
|
||||
|
||||
enum Coin {
|
||||
Penny,
|
||||
Nickel,
|
||||
Dime,
|
||||
Quarter(UsState),
|
||||
}
|
||||
|
||||
// ANCHOR: describe
|
||||
fn describe_state_quarter(coin: Coin) -> Option<String> {
|
||||
if let Coin::Quarter(state) = coin {
|
||||
if state.existed_in(1900) {
|
||||
Some(format!("{state:?} is pretty old, for America!"))
|
||||
} else {
|
||||
Some(format!("{state:?} is relatively new."))
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
// ANCHOR_END: describe
|
||||
|
||||
fn main() {
|
||||
if let Some(desc) = describe_state_quarter(Coin::Quarter(UsState::Alaska)) {
|
||||
println!("{desc}");
|
||||
}
|
||||
}
|
||||
6
listings/ch06-enums-and-pattern-matching/listing-06-08/Cargo.lock
generated
Normal file
6
listings/ch06-enums-and-pattern-matching/listing-06-08/Cargo.lock
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
[[package]]
|
||||
name = "enums"
|
||||
version = "0.1.0"
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
[package]
|
||||
name = "enums"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
@@ -0,0 +1,45 @@
|
||||
#[derive(Debug)] // so we can inspect the state in a minute
|
||||
enum UsState {
|
||||
Alabama,
|
||||
Alaska,
|
||||
// --snip--
|
||||
}
|
||||
|
||||
impl UsState {
|
||||
fn existed_in(&self, year: u16) -> bool {
|
||||
match self {
|
||||
UsState::Alabama => year >= 1819,
|
||||
UsState::Alaska => year >= 1959,
|
||||
// -- snip --
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum Coin {
|
||||
Penny,
|
||||
Nickel,
|
||||
Dime,
|
||||
Quarter(UsState),
|
||||
}
|
||||
|
||||
// ANCHOR: describe
|
||||
fn describe_state_quarter(coin: Coin) -> Option<String> {
|
||||
let state = if let Coin::Quarter(state) = coin {
|
||||
state
|
||||
} else {
|
||||
return None;
|
||||
};
|
||||
|
||||
if state.existed_in(1900) {
|
||||
Some(format!("{state:?} is pretty old, for America!"))
|
||||
} else {
|
||||
Some(format!("{state:?} is relatively new."))
|
||||
}
|
||||
}
|
||||
// ANCHOR_END: describe
|
||||
|
||||
fn main() {
|
||||
if let Some(desc) = describe_state_quarter(Coin::Quarter(UsState::Alaska)) {
|
||||
println!("{desc}");
|
||||
}
|
||||
}
|
||||
6
listings/ch06-enums-and-pattern-matching/listing-06-09/Cargo.lock
generated
Normal file
6
listings/ch06-enums-and-pattern-matching/listing-06-09/Cargo.lock
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
[[package]]
|
||||
name = "enums"
|
||||
version = "0.1.0"
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
[package]
|
||||
name = "enums"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
@@ -0,0 +1,43 @@
|
||||
#[derive(Debug)] // so we can inspect the state in a minute
|
||||
enum UsState {
|
||||
Alabama,
|
||||
Alaska,
|
||||
// --snip--
|
||||
}
|
||||
|
||||
impl UsState {
|
||||
fn existed_in(&self, year: u16) -> bool {
|
||||
match self {
|
||||
UsState::Alabama => year >= 1819,
|
||||
UsState::Alaska => year >= 1959,
|
||||
// -- snip --
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum Coin {
|
||||
Penny,
|
||||
Nickel,
|
||||
Dime,
|
||||
Quarter(UsState),
|
||||
}
|
||||
|
||||
// ANCHOR: describe
|
||||
fn describe_state_quarter(coin: Coin) -> Option<String> {
|
||||
let Coin::Quarter(state) = coin else {
|
||||
return None;
|
||||
};
|
||||
|
||||
if state.existed_in(1900) {
|
||||
Some(format!("{state:?} is pretty old, for America!"))
|
||||
} else {
|
||||
Some(format!("{state:?} is relatively new."))
|
||||
}
|
||||
}
|
||||
// ANCHOR_END: describe
|
||||
|
||||
fn main() {
|
||||
if let Some(desc) = describe_state_quarter(Coin::Quarter(UsState::Alaska)) {
|
||||
println!("{desc}");
|
||||
}
|
||||
}
|
||||
@@ -33,7 +33,7 @@
|
||||
- [Enums and Pattern Matching](ch06-00-enums.md)
|
||||
- [Defining an Enum](ch06-01-defining-an-enum.md)
|
||||
- [The `match` Control Flow Construct](ch06-02-match.md)
|
||||
- [Concise Control Flow with `if let`](ch06-03-if-let.md)
|
||||
- [Concise Control Flow with `if let` and `let else`](ch06-03-if-let.md)
|
||||
|
||||
## Basic Rust Literacy
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
## Concise Control Flow with `if let`
|
||||
## Concise Control Flow with `if let` and `let else`
|
||||
|
||||
The `if let` syntax lets you combine `if` and `let` into a less verbose way to
|
||||
handle values that match one pattern while ignoring the rest. Consider the
|
||||
@@ -62,8 +62,70 @@ Or we could use an `if let` and `else` expression, like this:
|
||||
{{#rustdoc_include ../listings/ch06-enums-and-pattern-matching/no-listing-14-count-and-announce-if-let-else/src/main.rs:here}}
|
||||
```
|
||||
|
||||
## Staying on the “happy path” with `let else`
|
||||
|
||||
One common pattern is to perform some computation when a value is present and
|
||||
return a default value otherwise. Continuing on with our example of coins with a
|
||||
`UsState` value, if we wanted to say something funny depending on how old the
|
||||
state on the quarter was, we might introduce a method on `UsState` to check the
|
||||
age of a state, like so:
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch06-enums-and-pattern-matching/listing-06-07/src/main.rs:state}}
|
||||
```
|
||||
|
||||
Then we might use `if let` to match on the type of coin, introducing a `state`
|
||||
variable within the body of the condition, as in Listing 6-7.
|
||||
|
||||
<Listing number="6-7" caption="Using" file-name="src/main.rs">
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch06-enums-and-pattern-matching/listing-06-07/src/main.rs:describe}}
|
||||
```
|
||||
|
||||
</Listing>
|
||||
|
||||
That gets the job done, but it has pushed the work into the body of the `if let`
|
||||
statement, and if the work to be done is more complicated, it might be hard to
|
||||
follow exactly how the top-level branches relate. We could also take advantage
|
||||
of the fact that expressions produce a value either to produce the `state` from
|
||||
the `if let` or to return early, as in Listing 6-8. (You could do similar with a
|
||||
`match`, of course!)
|
||||
|
||||
<Listing number="6-8" caption="Using `if let` to produce a value or return early." file-name="src/main.rs">
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch06-enums-and-pattern-matching/listing-06-08/src/main.rs:describe}}
|
||||
```
|
||||
|
||||
</Listing>
|
||||
|
||||
This is a bit annoying to follow in its own way, though! One branch of the `if
|
||||
let` produces a value, and the other one returns from the function entirely.
|
||||
|
||||
To make this common pattern nicer to express, Rust has `let`-`else`. The
|
||||
`let`-`else` syntax takes a pattern on the left side and an expression on the
|
||||
right, very similar to `if let`, but it does not have an `if` branch, only an
|
||||
`else` branch. If the pattern matches, it will bind the value from the pattern
|
||||
in the outer scope. If the pattern does *not* match, the program will flow into
|
||||
the `else` arm, which must return from the function.
|
||||
|
||||
In Listing 6-9, you can see how Listing 6-8 looks when using `let`-`else` in
|
||||
place of `if let`. Notice that it stays “on the happy path” in the main body of
|
||||
the function this way, without having significantly different control flow for
|
||||
two branches the way the `if let` did.
|
||||
|
||||
<Listing number="6-9" caption="Using `let`-`else` to clarify the flow through the function." file-name="src/main.rs">
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch06-enums-and-pattern-matching/listing-06-09/src/main.rs:describe}}
|
||||
```
|
||||
|
||||
</Listing>
|
||||
|
||||
If you have a situation in which your program has logic that is too verbose to
|
||||
express using a `match`, remember that `if let` is in your Rust toolbox as well.
|
||||
express using a `match`, remember that `if let` and `let else` are in your Rust
|
||||
toolbox as well.
|
||||
|
||||
## Summary
|
||||
|
||||
|
||||
@@ -10,11 +10,11 @@ a_value` because if the value in the `a_value` variable is `None` rather than
|
||||
|
||||
Function parameters, `let` statements, and `for` loops can only accept
|
||||
irrefutable patterns, because the program cannot do anything meaningful when
|
||||
values don’t match. The `if let` and `while let` expressions accept
|
||||
refutable and irrefutable patterns, but the compiler warns against
|
||||
irrefutable patterns because by definition they’re intended to handle possible
|
||||
failure: the functionality of a conditional is in its ability to perform
|
||||
differently depending on success or failure.
|
||||
values don’t match. The `if let` and `while let` expressions and the
|
||||
`let`-`else` statement accept refutable and irrefutable patterns, but the
|
||||
compiler warns against irrefutable patterns because by definition they’re
|
||||
intended to handle possible failure: the functionality of a conditional is in
|
||||
its ability to perform differently depending on success or failure.
|
||||
|
||||
In general, you shouldn’t have to worry about the distinction between refutable
|
||||
and irrefutable patterns; however, you do need to be familiar with the concept
|
||||
|
||||
Reference in New Issue
Block a user