1
0
mirror of https://github.com/google/comprehensive-rust.git synced 2025-12-17 21:27:49 +02:00

Rework introduction of pattern matching (#1843)

This commit is contained in:
Nicole L
2024-02-28 11:14:53 -08:00
committed by GitHub
parent 9b57c48615
commit c1e605df25
4 changed files with 43 additions and 31 deletions

View File

@@ -2,44 +2,55 @@
minutes: 5
---
# Destructuring
# Patterns and Destructuring
Destructuring is a way of extracting data from a data structure by writing a
pattern that is matched up to the data structure, binding variables to
subcomponents of the data structure.
You can destructure tuples and arrays by matching on their elements:
## Tuples
When working with tuples and other structured values it's common to want to
extract the inner values into local variables. This can be done manually by
directly accessing the inner values:
```rust,editable
fn main() {
describe_point((1, 0));
}
fn describe_point(point: (i32, i32)) {
match point {
(0, _) => println!("on Y axis"),
(_, 0) => println!("on X axis"),
(x, _) if x < 0 => println!("left of Y axis"),
(_, y) if y < 0 => println!("below X axis"),
_ => println!("first quadrant"),
}
fn print_tuple(tuple: (i32, i32)) {
let left = tuple.0;
let right = tuple.1;
println!("left: {left}, right: {right}");
}
```
## Arrays
However, Rust also supports using pattern matching to destructure a larger value
into its constituent parts:
```rust,editable
{{#include ../../third_party/rust-by-example/destructuring-arrays.rs}}
fn print_tuple(tuple: (i32, i32)) {
let (left, right) = tuple;
println!("left: {left}, right: {right}");
}
```
This works with any kind of structured value:
```rust,editable
struct Foo {
a: i32,
b: bool,
}
fn print_foo(foo: Foo) {
let Foo { a, b } = foo;
println!("a: {a}, b: {b}");
}
```
<details>
- Create a new array pattern using `_` to represent an element.
- Add more values to the array.
- Point out that how `..` will expand to account for different number of
elements.
- Show matching against the tail with patterns `[.., b]` and `[a@..,b]`
- The patterns used here are "irrefutable", meaning that the compiler can
statically verify that the value on the right of `=` has the same structure as
the pattern.
- A variable name is an irrefutable pattern that always matches any value, hence
why we can also use `let` to declare a single variable.
- Rust also supports using patterns in conditionals, allowing for equality
comparison and destructuring to happen at the same time. This form of pattern
matching will be discussed in more detail later.
- Edit the examples above to show the compiler error when the pattern doesn't
match the value being matched on.
</details>

View File

@@ -1,59 +0,0 @@
---
minutes: 10
---
# Pattern Matching
The `match` keyword lets you match a value against one or more _patterns_. The
comparisons are done from top to bottom and the first match wins.
The patterns can be simple values, similarly to `switch` in C and C++:
```rust,editable
#[rustfmt::skip]
fn main() {
let input = 'x';
match input {
'q' => println!("Quitting"),
'a' | 's' | 'w' | 'd' => println!("Moving around"),
'0'..='9' => println!("Number input"),
key if key.is_lowercase() => println!("Lowercase: {key}"),
_ => println!("Something else"),
}
}
```
The `_` pattern is a wildcard pattern which matches any value. The expressions
_must_ be irrefutable, meaning that it covers every possibility, so `_` is often
used as the final catch-all case.
Match can be used as an expression. Just like `if`, each match arm must have the
same type. The type is the last expression of the block, if any. In the example
above, the type is `()`.
A variable in the pattern (`key` in this example) will create a binding that can
be used within the match arm.
A match guard causes the arm to match only if the condition is true.
<details>
Key Points:
- You might point out how some specific characters are being used when in a
pattern
- `|` as an `or`
- `..` can expand as much as it needs to be
- `1..=5` represents an inclusive range
- `_` is a wild card
- Match guards as a separate syntax feature are important and necessary when we
wish to concisely express more complex ideas than patterns alone would allow.
- They are not the same as separate `if` expression inside of the match arm. An
`if` expression inside of the branch block (after `=>`) happens after the
match arm is selected. Failing the `if` condition inside of that block won't
result in other arms of the original `match` expression being considered.
- The condition defined in the guard applies to every expression in a pattern
with an `|`.
</details>