mirror of
https://github.com/darkicewolf50/RustBrock.git
synced 2025-06-14 20:44:17 -06:00
finished ch19.2 and started ch19.3
This commit is contained in:
parent
f0cbfdf024
commit
730b0091e8
20
.obsidian/workspace.json
vendored
20
.obsidian/workspace.json
vendored
@ -77,6 +77,20 @@
|
||||
"title": "Refutability"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "6d24bd7ca73ed503",
|
||||
"type": "leaf",
|
||||
"state": {
|
||||
"type": "markdown",
|
||||
"state": {
|
||||
"file": "Pattern Syntax.md",
|
||||
"mode": "source",
|
||||
"source": false
|
||||
},
|
||||
"icon": "lucide-file",
|
||||
"title": "Pattern Syntax"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "6ed9f182aa3d5f3e",
|
||||
"type": "leaf",
|
||||
@ -88,7 +102,7 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"currentTab": 3
|
||||
"currentTab": 5
|
||||
}
|
||||
],
|
||||
"direction": "vertical"
|
||||
@ -231,9 +245,10 @@
|
||||
"command-palette:Open command palette": false
|
||||
}
|
||||
},
|
||||
"active": "78cd7043d5d51ffa",
|
||||
"active": "6d24bd7ca73ed503",
|
||||
"lastOpenFiles": [
|
||||
"Pattern Matching.md",
|
||||
"Pattern Syntax.md",
|
||||
"Refutability.md",
|
||||
"Places Patterns Can Be Used.md",
|
||||
"Implementing OO Design Pattern.md",
|
||||
@ -258,7 +273,6 @@
|
||||
"Ref Cell Mutability.md",
|
||||
"Reference Counter Smart Pointer.md",
|
||||
"The Performance Closures and Iterators.md",
|
||||
"Improving The IO Project.md",
|
||||
"minigrep/src/lib.rs",
|
||||
"does_not_compile.svg",
|
||||
"Untitled.canvas",
|
||||
|
@ -29,6 +29,6 @@ This chapter is intended to be a reference on all things related to patterns.
|
||||
|
||||
We will cover:
|
||||
- Valid places to use patterns [Section Link Here]()
|
||||
- Difference between refutable and irrefutable patterns
|
||||
- different kinds of pattern syntax
|
||||
- Difference between refutable and irrefutable patterns [Section Link Here](./Refutability.md)
|
||||
- different kinds of pattern syntax [Section Link Here](./Pattern%20Syntax.md)
|
||||
By the end you will know how to use patterns to express many concepts in a clear way.
|
256
Pattern Syntax.md
Normal file
256
Pattern Syntax.md
Normal file
@ -0,0 +1,256 @@
|
||||
# Pattern Syntax
|
||||
Here we gather all the syntax valid in patterns and discuss why and when you might want to use each one.
|
||||
|
||||
## Matching Literals
|
||||
As you saw previously in Ch6, you can match patterns against literals directly.
|
||||
|
||||
Here is an example of this
|
||||
```rust
|
||||
let x = 1;
|
||||
|
||||
match x {
|
||||
1 => println!("one"),
|
||||
2 => println!("two"),
|
||||
3 => println!("three"),
|
||||
_ => println!("anything"),
|
||||
}
|
||||
```
|
||||
The code prints `one` because the value in `x` is 1.
|
||||
|
||||
This syntax is useful when you want your code to take an action if it gets a particular concrete value.
|
||||
|
||||
## Matching Named Variables
|
||||
Named variables are irrefutable patterns that match any value, and we have used them many times.
|
||||
|
||||
However, there is a complication when you use named variables in `match`, `if let`, or `while let` expressions.
|
||||
|
||||
Because each kinds of expression starts a new scope, variables declared as part of a pattern inside the expression will shadow those with the same name outside, as is the case with all variables.
|
||||
|
||||
Here we declare a variable named `x` with the value `Some(5)` and a variable `y` with the value `10`.
|
||||
|
||||
|
||||
Next we create a `match` expression on the value `x`.
|
||||
|
||||
Look at the patterns in the match arms and `println!` at the end, and try to figure out what the code will print before running this code or reading further.
|
||||
```rust
|
||||
let x = Some(5);
|
||||
let y = 10;
|
||||
|
||||
match x {
|
||||
Some(50) => println!("Got 50"),
|
||||
Some(y) => println!("Matched, y = {y}"),
|
||||
_ => println!("Default case, x = {x:?}"),
|
||||
}
|
||||
|
||||
println!("at the end: x = {x:?}, y = {y}");
|
||||
```
|
||||
Lets run through what happens when the `match` expression runs.
|
||||
|
||||
The pattern in the first match arm doesn't match the defined value of `x`, so the code continues.
|
||||
|
||||
The pattern in the second match arm introduces a new variable named `y` that will match any value inside a `Some` value.
|
||||
|
||||
Because we are in a new scope inside the `match` expression, this is a new `y` variable, not the `y` we declared at the beginning with the value 10.
|
||||
|
||||
This new `y` binding will match any value inside a `Some`, which is what we have in `x`.
|
||||
|
||||
Therefore the new `y` binds to the inner value of the `Some` in `x`.
|
||||
|
||||
That value is `5` so the expression for that arm executes and prints `Matched , y = 5`.
|
||||
|
||||
If `x` has been a `None` value instead of `Some(5)`, the patterns in the first two arms wouldn't have matched. so the value would have matched to the underscore.
|
||||
|
||||
We didn't introduce the `x` variable in the pattern of the underscore arm, so the `x` in the expression is still the outer `x` that hasn't been shadowed.
|
||||
|
||||
For this hypothetical case, the `maatch` would pint `Default case, x = None`.
|
||||
|
||||
When the `match` expression is done, its scope ends, and so does the scope of the inner `y`.
|
||||
|
||||
The last `println!` produces `at the end: x = Some(5), y = 10`.
|
||||
|
||||
To create a `match` expression that compares the values of the outer `x` and `y` rather than introducing a new variable which shadows the exiting `y` variable.
|
||||
|
||||
We would need to use a match guard conditional instead.
|
||||
|
||||
This will be covered later in the ch.
|
||||
|
||||
## Multiple Patterns
|
||||
You can match multiple patterns using the `|` syntax, which is the pattern *or* operator.
|
||||
|
||||
For example in the following code we match the value of `x` against the match arms, the first of which has an *or* option, meaning if the value of `x` matches either of the values in that arm, that arm's code will run
|
||||
```rust
|
||||
let x = 1;
|
||||
|
||||
match x {
|
||||
1 | 2 => println!("one or two"),
|
||||
3 => println!("three"),
|
||||
_ => println!("anything"),
|
||||
}
|
||||
```
|
||||
This code prints `one or two`.
|
||||
|
||||
## Matching Ranges of Values with `..=`
|
||||
The `..=` syntax allows us to match to an inclusive range of values.
|
||||
|
||||
Here when a pattern matches any of the values within the given range, that arm will execute
|
||||
```rust
|
||||
let x = 5;
|
||||
|
||||
match x {
|
||||
1..=5 => println!("one through five"),
|
||||
_ => println!("something else"),
|
||||
}
|
||||
```
|
||||
If `x` is 1, 2, 3, 4, or 5, the first arm will match.
|
||||
|
||||
This syntax is more convenient for multiple match values that using the `|` operator to express the same idea.
|
||||
|
||||
If we were to use `|` we would need to do `1 | 2 | 3 | 4 | 5`.
|
||||
|
||||
Specifying a range is much shorter, especially if we want to match, say any number between 1 and 1,000.
|
||||
|
||||
The compiler checks that the range isn't empty at compile time, because only types for which Rust can tell if a range is empty or not are `char` and numeric values, ranges are only allowed with numeric or `car` values.
|
||||
|
||||
Rust can tell that `'c'` is within the first pattern's range and prints `early ASCII letter`.
|
||||
|
||||
## Destructuring to Break Apart Values
|
||||
We can also use patterns to destructure structs, enums, and tuples to use different parts of these values.
|
||||
|
||||
We will walk through each value.
|
||||
|
||||
### Destructuring Structs
|
||||
This code shows a `Point` struct with two fields, `x` and `y`, that we can break apart using a patter with a `let` statement
|
||||
```rust
|
||||
struct Point {
|
||||
x: i32,
|
||||
y: i32,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let p = Point { x: 0, y: 7 };
|
||||
|
||||
let Point { x: a, y: b } = p;
|
||||
assert_eq!(0, a);
|
||||
assert_eq!(7, b);
|
||||
}
|
||||
```
|
||||
This creates the variables `a` and `b` that match the values of the `x` and `y` fields of the `p` struct.
|
||||
|
||||
This shows that the names of the variables in the pattern don't have to match the field names of the struct.
|
||||
|
||||
However it is common to match the variable names to the field names to make it easier to remember which variables came from which fields.
|
||||
|
||||
Because of this common usage, and because writing `let Point { x: x, y: y} = p;` contains a lot of duplication, Rust has a shorthand for patterns that match struct fields.
|
||||
|
||||
You only need to list the name of the struct field, and the variables created from the pattern will have the same names.
|
||||
|
||||
|
||||
This code behaves in the same way as om the code before, but the variables created in the `let` pattern are `x` and `y` instead of `a` and `b`.
|
||||
```rust
|
||||
struct Point {
|
||||
x: i32,
|
||||
y: i32,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let p = Point { x: 0, y: 7 };
|
||||
|
||||
let Point { x, y } = p;
|
||||
assert_eq!(0, x);
|
||||
assert_eq!(7, y);
|
||||
}
|
||||
```
|
||||
This code creates the variables `x` and `y` that match the `x` and `y` fields of the `p` variable.
|
||||
|
||||
The outcome is that the variables `x` and `y` contain the values form the `p` struct.
|
||||
|
||||
We can also destructure with literal values as part of the struct pattern rather than creating variables for all the fields.
|
||||
|
||||
Doing this allows us to test some of the fields for particular values while creating variables to destructure the other fields.
|
||||
|
||||
Here we have a `match` expression that separates `Point` values into three cases.
|
||||
|
||||
Points that lie directly on the `x` axis (which is true when `y = 0`)
|
||||
|
||||
On the `y` axis (`x = 0`)
|
||||
|
||||
Or Neither
|
||||
```rust
|
||||
fn main() {
|
||||
let p = Point { x: 0, y: 7 };
|
||||
|
||||
match p {
|
||||
Point { x, y: 0 } => println!("On the x axis at {x}"),
|
||||
Point { x: 0, y } => println!("On the y axis at {y}"),
|
||||
Point { x, y } => {
|
||||
println!("On neither axis: ({x}, {y})");
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
The first arm will match any point that lies on the `x` axis by specifying that the `y` field matches if its value matches the literal `0`.
|
||||
|
||||
The pattern still creates an `x` variable that we can use in the code for this arm.
|
||||
|
||||
The second arm matches any point on the `y` axis by specifying that the `x` field matches if its value is `0` and creates a variable `y` for the value of the `y` field.
|
||||
|
||||
The third arm doesn't specify any literals, so it matches any other `Point` and creates variables that we can use in the code for this arm.
|
||||
|
||||
Here the value `p` matches the second arm by virtue of `x` containing a 0, so this code will print `On the x axis at 0`.
|
||||
|
||||
### Destructuring Enums
|
||||
We have destructured enums before, but haven't explicitly discussed that the pattern to destructure an enum corresponds to the way the data stored within the enum is defined.
|
||||
|
||||
For example, here we use the `Message` enum from Ch6 and write a `match` with patterns that will destructure each inner value.
|
||||
```rust
|
||||
enum Message {
|
||||
Quit,
|
||||
Move { x: i32, y: i32 },
|
||||
Write(String),
|
||||
ChangeColor(i32, i32, i32),
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let msg = Message::ChangeColor(0, 160, 255);
|
||||
|
||||
match msg {
|
||||
Message::Quit => {
|
||||
println!("The Quit variant has no data to destructure.");
|
||||
}
|
||||
Message::Move { x, y } => {
|
||||
println!("Move in the x direction {x} and in the y direction {y}");
|
||||
}
|
||||
Message::Write(text) => {
|
||||
println!("Text message: {text}");
|
||||
}
|
||||
Message::ChangeColor(r, g, b) => {
|
||||
println!("Change the color to red {r}, green {g}, and blue {b}");
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
This code will print `Change the color to red 0, green 160, and blue 255`.
|
||||
|
||||
Try changing the value of `msg` to see the code form the other arms run.
|
||||
|
||||
For enum variants without any data, like `Message::Quit`.
|
||||
|
||||
We can't destructure the value any further.
|
||||
|
||||
We can only match on the literal `Message::Quit` value and no variables are in that pattern.
|
||||
|
||||
For struct-like enum variants, like `Message::Move`.
|
||||
|
||||
We can use a pattern similar to the pattern we specify to match structs.
|
||||
|
||||
After the variant name, we place curly brackets and then list the fields with variable so we break apart the pieces to use in the code for this arm.
|
||||
|
||||
We did use the shorthand form as we did before.
|
||||
|
||||
For tuple-like enum variants, like `Message::Write` that holds a tuple with one element and `Message::ChangeColor` that holds a tuple with three elements.
|
||||
|
||||
The pattern is similar to the pattern we specify to match tuples.
|
||||
|
||||
The number of variables in the pattern must match the number of elements in the variant we are matching.
|
||||
|
||||
### Destructuring Nested Structs and Enums
|
106
Refutability.md
106
Refutability.md
@ -1 +1,107 @@
|
||||
# Refutability: Whether a Pattern Might Fail to Match
|
||||
Patterns come in two forms:
|
||||
- Refutable
|
||||
- Irrefutable
|
||||
Patterns that will match for any possible value passed are *irrefutable*.
|
||||
|
||||
An example would be `x` in the statement `let x = 5;` because `x` matches anything and therefore cannot fail to match.
|
||||
|
||||
Patterns that can fail to match for some possible value are *refutable*.
|
||||
|
||||
An example would be `Some(x)` in the expression `if let Some(x) = a_value` because if the value in the `a_value` variable is `None` rather than `Some`, the `Some(x)` pattern will not match.
|
||||
|
||||
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 and the `let`-`else` statement accept refutable and irrefutable patterns, but the compiler warns against irrefutable patterns.
|
||||
|
||||
This is because by definition they are intended to handle possible failure.
|
||||
|
||||
The functionality of a conditional is in its ability to perform differently depending on success or failure.
|
||||
|
||||
Generally you shouldn't have to worry about the distinction between refutable and irrefutable patterns.
|
||||
|
||||
However you do need to be familiar with the concept of refutability so you can respond when you see it in an error message.
|
||||
|
||||
In these cases, you will need to change either the pattern or the construct you are using the pattern with, depending on the intended behavior of the code.
|
||||
|
||||
Now lets take a look at an example of what happens when you try to use a refutable pattern where Rust requires an irrefutable pattern and vice versa.
|
||||
|
||||
Here shows a `let` statement, but for the pattern we have specified `Some(x)`, a refutable pattern.
|
||||
|
||||
As expected, this code will not compile.
|
||||
```rust
|
||||
let Some(x) = some_option_value;
|
||||
```
|
||||
If `some_option_value` was a `None` value, it would fail to match the pattern `Some(x)`, meaning the pattern is refutable.
|
||||
|
||||
However, the `let` statement can only accept an irrefutable pattern because there is nothing valid the code can do with a `None` value.
|
||||
|
||||
At compile time, Rust will complain that we have tried to use a refutable pattern where an irrefutable pattern is required
|
||||
```
|
||||
$ cargo run
|
||||
Compiling patterns v0.1.0 (file:///projects/patterns)
|
||||
error[E0005]: refutable pattern in local binding
|
||||
--> src/main.rs:3:9
|
||||
|
|
||||
3 | let Some(x) = some_option_value;
|
||||
| ^^^^^^^ pattern `None` not covered
|
||||
|
|
||||
= note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
|
||||
= note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
|
||||
= note: the matched value is of type `Option<i32>`
|
||||
help: you might want to use `let else` to handle the variant that isn't matched
|
||||
|
|
||||
3 | let Some(x) = some_option_value else { todo!() };
|
||||
| ++++++++++++++++
|
||||
|
||||
For more information about this error, try `rustc --explain E0005`.
|
||||
error: could not compile `patterns` (bin "patterns") due to 1 previous error
|
||||
```
|
||||
Because we didn't cover (and couldn't cover) every valid value with the pattern `Some(x)`, Rust rightfully produces a compiler error.
|
||||
|
||||
If we have a refutable pattern where an irrefutable pattern is needed, we can fix it by changing the code that uses the pattern.
|
||||
|
||||
Instead of using `let`, we can use `if let`.
|
||||
|
||||
Then if the code doesn't match, the code will just skip the code in the curly brackets, giving a way to continue validly.
|
||||
|
||||
Here shows a fix to the code
|
||||
```rust
|
||||
if let Some(x) = some_option_value {
|
||||
println!("{x}");
|
||||
}
|
||||
```
|
||||
We given the code an out, this code is perfectly valid now.
|
||||
|
||||
However, if we give `if let` an irrefutable pattern, such as `x`, the compiler will give a warning.
|
||||
```rust
|
||||
if let x = 5 {
|
||||
println!("{x}");
|
||||
};
|
||||
```
|
||||
Rust complains that it doesn't make sense to use `if let` with an irrefutable pattern
|
||||
```
|
||||
$ cargo run
|
||||
Compiling patterns v0.1.0 (file:///projects/patterns)
|
||||
warning: irrefutable `if let` pattern
|
||||
--> src/main.rs:2:8
|
||||
|
|
||||
2 | if let x = 5 {
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= note: this pattern will always match, so the `if let` is useless
|
||||
= help: consider replacing the `if let` with a `let`
|
||||
= note: `#[warn(irrefutable_let_patterns)]` on by default
|
||||
|
||||
warning: `patterns` (bin "patterns") generated 1 warning
|
||||
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.39s
|
||||
Running `target/debug/patterns`
|
||||
5
|
||||
```
|
||||
For this reason, match arms must use refutable patterns, except for the last arm, which should match any remaining values with an irrefutable pattern.
|
||||
|
||||
Rust allows us to use an irrefutable pattern in a `match` with only one arm.
|
||||
|
||||
This syntax isn't particularly useful and could be replaced with a simpler `let` statement.
|
||||
|
||||
Next we will cover all the syntax we can use to create patterns [here](./Pattern%20Syntax.md)
|
Loading…
x
Reference in New Issue
Block a user