diff --git a/exercises/12_options/README.md b/exercises/12_options/README.md index 624572a3bc..f6a8f02007 100644 --- a/exercises/12_options/README.md +++ b/exercises/12_options/README.md @@ -12,6 +12,28 @@ Option types are very common in Rust code, as they have a number of uses: - Nullable pointers - Swapping things out of difficult situations +## Conditional pattern matching + +`if let` runs a block once when a value matches a pattern: + +```rust +if let PATTERN = EXPRESSION { + // The pattern matched. +} +``` + +`while let` repeats a block for as long as the value matches a pattern: + +```rust +while let PATTERN = EXPRESSION { + // The pattern matched. Try the expression again after this iteration. +} +``` + +The left side of `=` is a pattern, while the right side is the expression whose +result is matched. These constructs are useful when only one pattern matters. +Patterns can also be nested to match nested types such as `Option>`. + ## Further Information - [Option Enum Format](https://doc.rust-lang.org/book/ch10-01-syntax.html#in-enum-definitions) diff --git a/exercises/12_options/options2.rs b/exercises/12_options/options2.rs index 07c27c6ec2..e683e7c036 100644 --- a/exercises/12_options/options2.rs +++ b/exercises/12_options/options2.rs @@ -5,18 +5,39 @@ fn main() { #[cfg(test)] mod tests { #[test] - fn simple_option() { - let target = "rustlings"; - let optional_target = Some(target); + fn if_let() { + let text = "learning rust with rustlings"; + let optional_index = text.find("rustlings"); + let mut found_index = None; + let placeholder: Option = None; + assert_eq!(optional_index, Some(19)); - // TODO: Make this an if-let statement whose value is `Some`. - word = optional_target { - assert_eq!(word, target); + // TODO: Replace `placeholder` with the optional value defined above. + if let Some(index) = placeholder { + found_index = Some(index); } + + assert_eq!(found_index, Some(19)); + } + + #[test] + fn while_let() { + let mut numbers = vec![1, 2]; + numbers.push(3); + let mut sum = 0; + let placeholder: Option = None; + + // TODO: Replace `placeholder` with an expression that removes and + // returns the last element of `numbers`. + while let Some(number) = placeholder { + sum += number; + } + + assert_eq!(sum, 6); } #[test] - fn layered_option() { + fn nested_options() { let range = 10; let mut optional_integers: Vec> = vec![None]; @@ -26,11 +47,9 @@ mod tests { let mut cursor = range; - // TODO: Make this a while-let statement. Remember that `Vec::pop()` - // adds another layer of `Option`. You can do nested pattern matching - // in if-let and while-let statements. - integer = optional_integers.pop() { - assert_eq!(integer, cursor); + // TODO: Add another `Some` to the pattern so that the loop stops when + // it encounters the `None` stored in the vector. + while let Some(_) = optional_integers.pop() { cursor -= 1; } diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 97c1ccaee9..3b1d9b0b9c 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -604,16 +604,23 @@ it doesn't panic in your face later?""" name = "options2" dir = "12_options" hint = """ -Check out: +`if let PATTERN = EXPRESSION` runs a block once if the expression matches the +pattern. `while let PATTERN = EXPRESSION` repeats the block for as long as it +matches. -- https://doc.rust-lang.org/rust-by-example/flow_control/if_let.html -- https://doc.rust-lang.org/rust-by-example/flow_control/while_let.html +The first two statements already contain the complete `if let` and `while let` +syntax. Replace each `placeholder` with the `Option`-producing expression +described in its TODO comment. -Remember that `Option`s can be nested in if-let and while-let statements. +For the last TODO, remember that popping from a `Vec>` returns an +`Option>`: one `Option` comes from `pop`, and the other is stored in +the vector. Match both layers so the loop stops on either `None`. -For example: `if let Some(Some(x)) = y` +More information: -Also see `Option::flatten`""" +- https://doc.rust-lang.org/rust-by-example/flow_control/if_let.html +- https://doc.rust-lang.org/rust-by-example/flow_control/while_let.html +- https://doc.rust-lang.org/std/option/enum.Option.html#method.flatten""" [[exercises]] name = "options3" diff --git a/solutions/12_options/options2.rs b/solutions/12_options/options2.rs index 0f24665ff5..299832291f 100644 --- a/solutions/12_options/options2.rs +++ b/solutions/12_options/options2.rs @@ -5,18 +5,36 @@ fn main() { #[cfg(test)] mod tests { #[test] - fn simple_option() { - let target = "rustlings"; - let optional_target = Some(target); + fn if_let() { + let text = "learning rust with rustlings"; + let optional_index = text.find("rustlings"); + let mut found_index = None; + assert_eq!(optional_index, Some(19)); - // if-let - if let Some(word) = optional_target { - assert_eq!(word, target); + // Run the block only when `optional_index` contains an index. + if let Some(index) = optional_index { + found_index = Some(index); } + + assert_eq!(found_index, Some(19)); + } + + #[test] + fn while_let() { + let mut numbers = vec![1, 2]; + numbers.push(3); + let mut sum = 0; + + // `pop` returns `Some(number)` until the vector is empty. + while let Some(number) = numbers.pop() { + sum += number; + } + + assert_eq!(sum, 6); } #[test] - fn layered_option() { + fn nested_options() { let range = 10; let mut optional_integers: Vec> = vec![None]; @@ -26,9 +44,9 @@ mod tests { let mut cursor = range; - // while-let with nested pattern matching - while let Some(Some(integer)) = optional_integers.pop() { - assert_eq!(integer, cursor); + // The outer `Some` matches `pop`, and the inner one matches the value + // stored in the vector. The loop stops on either layer of `None`. + while let Some(Some(_)) = optional_integers.pop() { cursor -= 1; }