finished ch19.2 and started ch19.3
All checks were successful
Test Gitea Actions / first (push) Successful in 21s
Test Gitea Actions / check-code (push) Successful in 18s
Test Gitea Actions / test (push) Successful in 18s
Test Gitea Actions / documentation-check (push) Successful in 17s

This commit is contained in:
darkicewolf50 2025-04-09 17:00:42 -06:00
parent f0cbfdf024
commit 730b0091e8
4 changed files with 381 additions and 5 deletions

View File

@ -77,6 +77,20 @@
"title": "Refutability" "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", "id": "6ed9f182aa3d5f3e",
"type": "leaf", "type": "leaf",
@ -88,7 +102,7 @@
} }
} }
], ],
"currentTab": 3 "currentTab": 5
} }
], ],
"direction": "vertical" "direction": "vertical"
@ -231,9 +245,10 @@
"command-palette:Open command palette": false "command-palette:Open command palette": false
} }
}, },
"active": "78cd7043d5d51ffa", "active": "6d24bd7ca73ed503",
"lastOpenFiles": [ "lastOpenFiles": [
"Pattern Matching.md", "Pattern Matching.md",
"Pattern Syntax.md",
"Refutability.md", "Refutability.md",
"Places Patterns Can Be Used.md", "Places Patterns Can Be Used.md",
"Implementing OO Design Pattern.md", "Implementing OO Design Pattern.md",
@ -258,7 +273,6 @@
"Ref Cell Mutability.md", "Ref Cell Mutability.md",
"Reference Counter Smart Pointer.md", "Reference Counter Smart Pointer.md",
"The Performance Closures and Iterators.md", "The Performance Closures and Iterators.md",
"Improving The IO Project.md",
"minigrep/src/lib.rs", "minigrep/src/lib.rs",
"does_not_compile.svg", "does_not_compile.svg",
"Untitled.canvas", "Untitled.canvas",

View File

@ -29,6 +29,6 @@ This chapter is intended to be a reference on all things related to patterns.
We will cover: We will cover:
- Valid places to use patterns [Section Link Here]() - Valid places to use patterns [Section Link Here]()
- Difference between refutable and irrefutable patterns - Difference between refutable and irrefutable patterns [Section Link Here](./Refutability.md)
- different kinds of pattern syntax - 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. By the end you will know how to use patterns to express many concepts in a clear way.

256
Pattern Syntax.md Normal file
View 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

View File

@ -1 +1,107 @@
# Refutability: Whether a Pattern Might Fail to Match # 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)