From 730b0091e889f309c27a464e9f00a4e3074cbd30 Mon Sep 17 00:00:00 2001 From: darkicewolf50 Date: Wed, 9 Apr 2025 17:00:42 -0600 Subject: [PATCH] finished ch19.2 and started ch19.3 --- .obsidian/workspace.json | 20 ++- Pattern Matching.md | 4 +- Pattern Syntax.md | 256 +++++++++++++++++++++++++++++++++++++++ Refutability.md | 106 ++++++++++++++++ 4 files changed, 381 insertions(+), 5 deletions(-) create mode 100644 Pattern Syntax.md diff --git a/.obsidian/workspace.json b/.obsidian/workspace.json index fca6dba..6eabf0a 100644 --- a/.obsidian/workspace.json +++ b/.obsidian/workspace.json @@ -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", diff --git a/Pattern Matching.md b/Pattern Matching.md index 3dd1dbb..6e244f6 100644 --- a/Pattern Matching.md +++ b/Pattern Matching.md @@ -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. \ No newline at end of file diff --git a/Pattern Syntax.md b/Pattern Syntax.md new file mode 100644 index 0000000..d4f58f5 --- /dev/null +++ b/Pattern Syntax.md @@ -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 diff --git a/Refutability.md b/Refutability.md index 0811067..f59bdff 100644 --- a/Refutability.md +++ b/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` +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) \ No newline at end of file