Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions exercises/12_options/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<Option<T>>`.

## Further Information

- [Option Enum Format](https://doc.rust-lang.org/book/ch10-01-syntax.html#in-enum-definitions)
Expand Down
43 changes: 31 additions & 12 deletions exercises/12_options/options2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<usize> = 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<i32> = 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<Option<i8>> = vec![None];

Expand All @@ -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;
}

Expand Down
19 changes: 13 additions & 6 deletions rustlings-macros/info.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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<Option<i8>>` returns an
`Option<Option<i8>>`: 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"
Expand Down
38 changes: 28 additions & 10 deletions solutions/12_options/options2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Option<i8>> = vec![None];

Expand All @@ -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;
}

Expand Down