diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 9a94687c9f8b..039cbf83de11 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -81,6 +81,9 @@ - [Destructuring Structs](pattern-matching/destructuring-structs.md) - [Destructuring Enums](pattern-matching/destructuring-enums.md) - [Let Control Flow](pattern-matching/let-control-flow.md) + - [`if let` Expressions](pattern-matching/let-control-flow/if-let.md) + - [`while let` Statements](pattern-matching/let-control-flow/while-let.md) + - [`let else`](pattern-matching/let-control-flow/let-else.md) - [Exercise: Expression Evaluation](pattern-matching/exercise.md) - [Solution](pattern-matching/solution.md) - [Methods and Traits](methods-and-traits.md) diff --git a/src/pattern-matching/let-control-flow.md b/src/pattern-matching/let-control-flow.md index 046a3ca3b2d0..dcf0506037e6 100644 --- a/src/pattern-matching/let-control-flow.md +++ b/src/pattern-matching/let-control-flow.md @@ -8,123 +8,5 @@ Rust has a few control flow constructs which differ from other languages. They are used for pattern matching: - `if let` expressions -- `let else` expressions - `while let` expressions - -# `if let` expressions - -The -[`if let` expression](https://doc.rust-lang.org/reference/expressions/if-expr.html#if-let-expressions) -lets you execute different code depending on whether a value matches a pattern: - -```rust,editable -use std::time::Duration; - -fn sleep_for(secs: f32) { - if let Ok(duration) = Duration::try_from_secs_f32(secs) { - std::thread::sleep(duration); - println!("slept for {duration:?}"); - } -} - -fn main() { - sleep_for(-10.0); - sleep_for(0.8); -} -``` - -# `let else` expressions - -For the common case of matching a pattern and returning from the function, use -[`let else`](https://doc.rust-lang.org/rust-by-example/flow_control/let_else.html). -The "else" case must diverge (`return`, `break`, or panic - anything but falling -off the end of the block). - -```rust,editable -fn hex_or_die_trying(maybe_string: Option) -> Result { - // TODO: The structure of this code is difficult to follow -- rewrite it with let-else! - if let Some(s) = maybe_string { - if let Some(first_byte_char) = s.chars().next() { - if let Some(digit) = first_byte_char.to_digit(16) { - Ok(digit) - } else { - return Err(String::from("not a hex digit")); - } - } else { - return Err(String::from("got empty string")); - } - } else { - return Err(String::from("got None")); - } -} - -fn main() { - println!("result: {:?}", hex_or_die_trying(Some(String::from("foo")))); -} -``` - -Like with `if let`, there is a -[`while let`](https://doc.rust-lang.org/reference/expressions/loop-expr.html#predicate-pattern-loops) -variant which repeatedly tests a value against a pattern: - - - -```rust,editable -fn main() { - let mut name = String::from("Comprehensive Rust 🦀"); - while let Some(c) = name.pop() { - println!("character: {c}"); - } - // (There are more efficient ways to reverse a string!) -} -``` - -Here -[`String::pop`](https://doc.rust-lang.org/stable/std/string/struct.String.html#method.pop) -returns `Some(c)` until the string is empty, after which it will return `None`. -The `while let` lets us keep iterating through all items. - -
- -## if-let - -- Unlike `match`, `if let` does not have to cover all branches. This can make it - more concise than `match`. -- A common usage is handling `Some` values when working with `Option`. -- Unlike `match`, `if let` does not support guard clauses for pattern matching. - -## let-else - -`if-let`s can pile up, as shown. The `let-else` construct supports flattening -this nested code. Rewrite the awkward version for students, so they can see the -transformation. - -The rewritten version is: - -```rust -fn hex_or_die_trying(maybe_string: Option) -> Result { - let Some(s) = maybe_string else { - return Err(String::from("got None")); - }; - - let Some(first_byte_char) = s.chars().next() else { - return Err(String::from("got empty string")); - }; - - let Some(digit) = first_byte_char.to_digit(16) else { - return Err(String::from("not a hex digit")); - }; - - return Ok(digit); -} -``` - -# while-let - -- Point out that the `while let` loop will keep going as long as the value - matches the pattern. -- You could rewrite the `while let` loop as an infinite loop with an if - statement that breaks when there is no value to unwrap for `name.pop()`. The - `while let` provides syntactic sugar for the above scenario. - -
+- `let else` expressions diff --git a/src/pattern-matching/let-control-flow/if-let.md b/src/pattern-matching/let-control-flow/if-let.md new file mode 100644 index 000000000000..2f5717f2fa69 --- /dev/null +++ b/src/pattern-matching/let-control-flow/if-let.md @@ -0,0 +1,31 @@ +# `if let` Expressions + +The +[`if let` expression](https://doc.rust-lang.org/reference/expressions/if-expr.html#if-let-expressions) +lets you execute different code depending on whether a value matches a pattern: + +```rust,editable +use std::time::Duration; + +fn sleep_for(secs: f32) { + if let Ok(duration) = Duration::try_from_secs_f32(secs) { + std::thread::sleep(duration); + println!("slept for {duration:?}"); + } +} + +fn main() { + sleep_for(-10.0); + sleep_for(0.8); +} +``` + +
+ +- Unlike `match`, `if let` does not have to cover all branches. This can make it + more concise than `match`. +- A common usage is handling `Some` values when working with `Option`. +- Unlike `match`, `if let` does not support guard clauses for pattern matching. +- With an `else` clause, this can be used as an expression. + +
diff --git a/src/pattern-matching/let-control-flow/let-else.md b/src/pattern-matching/let-control-flow/let-else.md new file mode 100644 index 000000000000..aceef48e5f8f --- /dev/null +++ b/src/pattern-matching/let-control-flow/let-else.md @@ -0,0 +1,57 @@ +# `let else` Statements + +For the common case of matching a pattern and returning from the function, use +[`let else`](https://doc.rust-lang.org/rust-by-example/flow_control/let_else.html). +The "else" case must diverge (`return`, `break`, or panic - anything but falling +off the end of the block). + +```rust,editable +fn hex_or_die_trying(maybe_string: Option) -> Result { + // TODO: The structure of this code is difficult to follow -- rewrite it with let-else! + if let Some(s) = maybe_string { + if let Some(first_byte_char) = s.chars().next() { + if let Some(digit) = first_byte_char.to_digit(16) { + Ok(digit) + } else { + Err(String::from("not a hex digit")) + } + } else { + Err(String::from("got empty string")) + } + } else { + Err(String::from("got None")) + } +} + +fn main() { + println!("result: {:?}", hex_or_die_trying(Some(String::from("foo")))); +} +``` + +
+ +`if-let`s can pile up, as shown. The `let-else` construct supports flattening +this nested code. Rewrite the awkward version for students, so they can see the +transformation. + +The rewritten version is: + +```rust +fn hex_or_die_trying(maybe_string: Option) -> Result { + let Some(s) = maybe_string else { + return Err(String::from("got None")); + }; + + let Some(first_byte_char) = s.chars().next() else { + return Err(String::from("got empty string")); + }; + + let Some(digit) = first_byte_char.to_digit(16) else { + return Err(String::from("not a hex digit")); + }; + + Ok(digit) +} +``` + +
diff --git a/src/pattern-matching/let-control-flow/while-let.md b/src/pattern-matching/let-control-flow/while-let.md new file mode 100644 index 000000000000..19628fd3b211 --- /dev/null +++ b/src/pattern-matching/let-control-flow/while-let.md @@ -0,0 +1,34 @@ +# `while let` Statements + +Like with `if let`, there is a +[`while let`](https://doc.rust-lang.org/reference/expressions/loop-expr.html#predicate-pattern-loops) +variant which repeatedly tests a value against a pattern: + + + +```rust,editable +fn main() { + let mut name = String::from("Comprehensive Rust 🦀"); + while let Some(c) = name.pop() { + println!("character: {c}"); + } + // (There are more efficient ways to reverse a string!) +} +``` + +Here +[`String::pop`](https://doc.rust-lang.org/stable/std/string/struct.String.html#method.pop) +returns `Some(c)` until the string is empty, after which it will return `None`. +The `while let` lets us keep iterating through all items. + +
+ +- Point out that the `while let` loop will keep going as long as the value + matches the pattern. +- You could rewrite the `while let` loop as an infinite loop with an if + statement that breaks when there is no value to unwrap for `name.pop()`. The + `while let` provides syntactic sugar for the above scenario. +- This form cannot be used as an expression, because it may have no value if the + condition is false. + +