From 209287c3e92b3e386dc281232c9090a64639f8e9 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Thu, 23 Feb 2017 15:52:47 -0500 Subject: [PATCH 01/22] Chapter 18: Patterns Fixes #468 --- second-edition/src/ch18-00-patterns.md | 250 +++++++++++++++++-------- 1 file changed, 177 insertions(+), 73 deletions(-) diff --git a/second-edition/src/ch18-00-patterns.md b/second-edition/src/ch18-00-patterns.md index 12ae467601..f331a5ad28 100644 --- a/second-edition/src/ch18-00-patterns.md +++ b/second-edition/src/ch18-00-patterns.md @@ -1,14 +1,26 @@ # Patterns -We've actually used patterns a few times so far: they're used in `let` -statements, in function parameters, and in the `match` expression. Patterns have -a lot more abilities than we have demonstrated so far, so we'll cover some of -the most commonly used ones in this section. Any of these abilities work in any -place where a pattern is used. +One of Rust's more subtle features is the simple *pattern*. Patterns let us +pick apart complex strucutres and do all kinds of fun things with them. In this +chapter, we'll start off by showing you some very basic patterns, and then all +of the places that you can use patterns. Finally, we'll have a reference-like +elaboration of all of the different kinds of patterns, with examples to show +you how to use them. -## `let` statements +## What are patterns? -A basic `let` statement has this form: +Patterns are a special syntax within Rust for matching against the structure of +your types, complex or simple. We take some value, compare it against the +pattern, and then do something with it. For example, consider this simple +variable assignment with `let`: + +```rust +let x = 5; +``` + +We've done this hundreds of times throughout this book. Well, you didn't +realize it, but you were using patterns! A `let` statement looks like this, +gramatically: ```text let PATTERN = EXPRESSION; @@ -17,88 +29,176 @@ let PATTERN = EXPRESSION; We've seen statements like `let x = 5;` with a variable name in the `PATTERN` slot; a variable name is just a particularly humble form of pattern. -Let’s try a more complex pattern. Change our example program to this: +With `let`, we compare the expression against the pattern, and assign any +names we find. So for example, in our `let x = 5;` case, `x` is a pattern +that says "bind what matches here to the variable `x`. And since that's the +whole pattern, it effecitvely means "bind everything to the variable `x`." -Filename: src/main.rs +To see this a bit more clearly, consider this code: ```rust -fn main() { - let (x, y) = (5, 6); +let (x, y, z) = (1, 2, 3); +``` - println!("The value of x is: {}", x); - println!("The value of y is: {}", y); -} +Here, we have a tuple that we're matching against a pattern. Rust will compare +the value `(1, 2, 3)` to the pattern `(x, y, z)`, and see that it's valid. In +this case, it will bind `x` to `1`, `y` to `2`, and `z` to `3`. + +We can mix and match and nest patterns: you can think of this tuple pattern as +nesting three individual variable patterns inside of it. Or something like: + +```rust +let ((one, two), name, (three, four), five) = ((1, 2), "hello", (3, 4), 5); ``` -And run it with `cargo run`: +Where we nest tons of things inside of each other. + +## Refutability + +Patterns come in two forms: refutable, and irrefutable. Patterns which cannot +fail to match are "irrefutable", and patterns which can fail to match are said +to be "refutable". + +Consider our `let x = 5;` example. `let` takes an irrefutable pattern, and this +is true for `x`: since it matches anything, it can't fail to match. Consider +trying to match an enum with `let`, something like + +```rust,ignore +let Some(x) = some_option_value; +``` + +This can't work, and Rust will complain: ```text -$ cargo run - Compiling patterns v0.1.0 (file:///projects/patterns) - Running `target/debug/patterns` -The value of x is: 5 -The value of y is: 6 +error[E0005]: refutable pattern in local binding: `None` not covered + --> :3:5 + | +3 | let Some(x) = some_option_value; + | ^^^^^^^ pattern `None` not covered +``` + +We didn't cover (and couldn't cover!) every valid option here, and so Rust will +rightfully complain. What should it have done with the `None` case? + +On the other hand, `if let` takes a refutable pattern: + +```rust,ignore +if let Some(x) = some_option_value { + println!("{}", x); +} +``` + +And if you give it an irrefutable one... + +```rust +if let x = 5 { + println!("{}", x); +}; ``` -We’ve created two variables with one `let`! Here’s our pattern: +...it will complain: ```text -(x, y) +error[E0162]: irrefutable if-let pattern + --> :2:8 + | +2 | if let x = 5 { + | ^ irrefutable pattern ``` -And here’s the value: +In general, you shouldn't have to worry about this distinction; just be +familliar with the word when you see it, and realize that you need to change +either the pattern, or the construct you're using the pattern with. + +## Where can I use patterns? + +Patterns pop up in a number of places in Rust. You've been using them a lot +without realizing it! + +### `let` statements + +We talked about this above, the basic grammar of a `let` statement is: ```text -(5, 6) +let PATTERN = EXPRESSION; ``` -As you can see, the two line up visually, and so `let` binds the variable `x` -to the value `5` and `y` to `6`. We could have used two `let` statements as -well: +Many people don't realize that you can use any patterns on the left hand side +of a `let` statement, not just binding patterns. You'll see more examples of +this later in this chapter. -```rust -fn main() { - let x = 5; - let y = 6; +`let`, as mentioned above as well, takes an irrefutable pattern. + +### `match` arms + +Patterns are used very heavily by `match` expressions, particularly in match +arms. + +```text +match VALUE { + PATTERN => EXPRESSION, + PATTERN => EXPRESSION, + PATTERN => EXPRESSION, } ``` -In simple cases like this, two `let`s may be clearer, but in others, creating -multiple variables at once is nice. As we become more proficient in Rust, we’ll -figure out which style is better, but it’s mostly a judgment call. +These patterns are refuatble. However... + +#### Exhaustiveness and `_` -## Type annotations +`match` expressions are required to be exhaustive. So when we put all of the +patterns together, they must cover all of our bases. One way to ensure you have +every possibility covered is to introduce an irrefutable pattern, like a +binding. Since it is irrefutable, it can never fail, and so covers every case +by definition. -Most of the time, Rust uses *type inference*, meaning that it attempts to infer -the types of your variables rather than you having to declare them explicitly -even though Rust is a statically typed language. Occasionally, Rust won't have -enough information to infer the type of your value, and you will need to add a -type annotation in with the pattern. +There's an additional irrefutable pattern that's often used in this case +though: `_`. It matches anything, but it never binds any variables. This can be +useful when you only want to do things for some patterns, but ignore the rest, +for example. -Here’s what a `let` statement with a *type annotation* looks like: +#### Shadowing in patterns + +As with all variables, those declared by a pattern will shadow variables +outside of the `match` construct: ```rust -let x: i32 = 5; +let x = Some(5); + +match x { + Some(x) => { }, // x is an i32 here, not an Option + None => (), +} ``` -We can add a colon, followed by the type name. Here’s the structure of a `let` -statement with a type annotation: +### Function Arguments -```text -let PATTERN: TYPE = VALUE; +Similarly to `let`, function arguments are also irrefutable patterns: + +```rust +fn foo(x: i32) { + // code goes here +} ``` -Note that the colon and the `TYPE` go *after* the `PATTERN`, not in the pattern -itself. As an example, here’s our more complex pattern with two variables: +The `x` part is a pattern! If we didn't want to use the argument in the body of +our function, we could use `_` for example: ```rust -let (x, y): (i32, i32) = (5, 6); +fn foo(_: i32) { + // code goes here +} ``` -Just like we match up the `VALUE` with the `PATTERN`, we match up the `TYPE` -with the `PATTERN`. +Normally, you just wouldn't declare an argument, but maybe you're implementing +a trait, or need a certain type signature for some other reason. This lets you +not have to use the argument. + +## Kinds of pattern -## Literals & _ +Here's a list of all of the different types of patterns. + +### Literals & _ You can match against literals directly, and `_` acts as an ‘any’ case: @@ -115,7 +215,9 @@ match x { This prints `one`. -# Multiple patterns +Literals are refutable patterns, but `_` is irrefutable. + +### Multiple patterns You can match multiple patterns with `|`: @@ -131,7 +233,7 @@ match x { This prints `one or two`. -## ref and ref mut +### ref and ref mut Usually, when you match against a pattern, variables are bound to a value. This means you'll end up moving the value into the `match`: @@ -178,7 +280,7 @@ match name { println!("name is: {:?}", name); ``` -## Destructuring +### Destructuring Patterns can be used to destructure structs and enums: @@ -210,21 +312,7 @@ match origin { } ``` -## Shadowing - -As with all variables, those declared by a pattern will shadow variables -outside of the `match` construct: - -```rust -let x = Some(5); - -match x { - Some(x) => { }, // x is an i32 here, not an Option - None => (), -} -``` - -## Ignoring values +### Ignoring values We discussed using `_` as a whole pattern to ignore it above, but you can also use `_` inside of another pattern to ignore just part of it: @@ -264,7 +352,7 @@ match origin { } ``` -## Ranges +### Ranges You can match a range of values with `...`: @@ -289,7 +377,7 @@ match x { } ``` -## Guards +### Guards You can introduce ‘match guards’ with `if`: @@ -328,6 +416,22 @@ not this: 4 | (5 if y) => ... ``` -## Bindings +### Bindings You can bind values to names with `@`: + +```rust +enum Message { + Hello { id: i32 }, +} + +let msg = Message::Hello { id: 5 }; + +match msg { + Message::Hello { id: id @ 3...7 } => println!("{}", id), + _ => (), +} +``` + +In this case, we want to compare `id` against the range `3...7`, but we also +want to save the actual value of `id`. From fc9a3144dae2e8b6f4d32a9eee45635044c4988e Mon Sep 17 00:00:00 2001 From: "Carol (Nichols || Goulding)" Date: Mon, 27 Feb 2017 12:54:03 -0500 Subject: [PATCH 02/22] Carol edits --- second-edition/src/ch18-00-patterns.md | 322 ++++++++++++++++--------- 1 file changed, 210 insertions(+), 112 deletions(-) diff --git a/second-edition/src/ch18-00-patterns.md b/second-edition/src/ch18-00-patterns.md index f331a5ad28..ecc81f6ac4 100644 --- a/second-edition/src/ch18-00-patterns.md +++ b/second-edition/src/ch18-00-patterns.md @@ -1,26 +1,141 @@ -# Patterns +# Patterns Match the Structure of Values -One of Rust's more subtle features is the simple *pattern*. Patterns let us -pick apart complex strucutres and do all kinds of fun things with them. In this -chapter, we'll start off by showing you some very basic patterns, and then all -of the places that you can use patterns. Finally, we'll have a reference-like -elaboration of all of the different kinds of patterns, with examples to show -you how to use them. +Patterns are a special syntax within Rust for matching against the structure of +our types, complex or simple. We take some value and compare it against the +pattern. If the pattern matches our value, then we do something with the value +parts. Recall chapter 6 when we discussed the `match` expression that uses +patterns like a coin sorting machine: patterns describe the "shape" of the data +we're working with. We can name pieces within the shape, like we named the +state that appeared on quarters in Chapter 6, and if the data fits the shape, +we can use the named pieces. + +This chapter is a reference on all things related to patterns. We'll cover the +difference between *refutable* and *irrefutable* patterns, the valid places to +use patterns, and the different kinds of pattern syntax that you might see and +what it means. + +## Refutability: Whether a Pattern Might Fail to Match + +Patterns come in two forms: refutable and irrefutable. Patterns which cannot +fail to match for any possible value are *irrefutable*, and patterns which can +fail to match for some possible value are said to be *refutable*. Some places +where you can use patterns, like `let` statements (which we'll get into more in +the next section in this chapter), are restricted to only accept irrefutable +patterns, since there's nothing correct the program could do if the pattern +fails to match. Other places you can use patterns, like `if let` expressions, +are restricted to only accept refutable patterns, since they're made to handle +possible failure and we wouldn't need their functionality if the pattern could +never fail. + +In general, you shouldn't have to worry about the distinction between refutable +and irrefutable patterns; just be familliar with the concept of refutability +when you see it mentioned in an error message. When you get an error message +involving refutability, you'll need to change either the pattern or the +construct you're using the pattern with, depending on your intentions for the +behavior of the code. + +Let's now go through all the different places we can use patterns, and we'll +mention whether each place takes a refutable or irrefutable pattern as we do so. + +## All the Places Patterns May be Used -## What are patterns? +Patterns pop up in a number of places in Rust. You've been using them a lot +without realizing it! This section is a reference to all the places where +patterns are valid. -Patterns are a special syntax within Rust for matching against the structure of -your types, complex or simple. We take some value, compare it against the -pattern, and then do something with it. For example, consider this simple -variable assignment with `let`: +### `match` Arms + +As we discussed in Chapter 6, a common place patterns are used is in the arms +of `match` expressions. Formally, `match` expressions are defined as the +keyword `match`, a value, and one or more arms that consist of a pattern and an +expression to run if the value matches that arm's pattern: + +```text +match VALUE { + PATTERN => EXPRESSION, + PATTERN => EXPRESSION, + PATTERN => EXPRESSION, +} +``` + +Generally, match arms use refutable patterns. A `match` with only one arm whose +pattern is irrefutable is allowed, but it's not particularly useful and could +be replaced with a simpler `let` statement. There's one caveat with patterns +and `match` expressions, however: the set of all patterns in a `match` must be +*exhaustive* and cover all possible cases. + +#### Exhaustiveness and `_` + +`match` expressions are required to be exhaustive. When we put all of the +patterns in the arms together, all possibilities for the value in the `match` +expression must be accounted for. One way to ensure you have every possibility +covered is to have an irrefutable pattern for the last arm, like a variable +name. Since a name matching any value is an irrefutable pattern, it can never +fail and thus covers every case remaining after the previous arms' patterns by +definition. + +There's an additional irrefutable pattern that's often used in the last match +arm: `_`. It matches anything, but it never binds any variables. This can be +useful when you only want to do things for some patterns, but ignore the rest, +for example. + +#### Shadowing in patterns + +As with all variables, those declared by a pattern will shadow variables +outside of the `match` construct since a `match` starts a new scope. In the +next example, we declare a variable named `x` with the value `Some(5)` and a +variable `y` with the value `10`. Then we have a `match` expression on the +value `x`. Take a look at the patterns in the match arms and the `println!` at +the end, and make a guess about what will be printed before running this code +or reading further: + +```rust +fn main() { + 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 = {:?}, y = {:?}", x, y); +} +``` + +The first match arm has the pattern `Some(50)`, and the value in `x` +(`Some(5)`) does not match `Some(50)`, so we continue. In the second match arm, +the pattern `Some(y)` introduces a new variable name `y` that will match any +value inside a `Some` value. Because we're in a new scope inside the `match` +expression, this is a new variable, not the `y` we declared at the beginning +that has the value 10. So the new `y` binding will match any value inside a +`Some`, which is what we have in `x`, so we execute the expression for that arm +and print `Matched, y = 5` since this `y` binds to the inner value of the +`Some` in `x`, which is 5. + +If `x` had been a `None` value instead of `Some(5)`, we would have matched the +underscore since the other two arms' patterns would not have matched. In the +expression for that match arm, since we did not introduce an `x` variable in +the pattern of the arm, this `x` is still the outer `x` that has not been +shadowed. In this hypothetical case, the `match` would print `Default case, x = +None`. + +Once the `match` expression is over, its scope ends, and so does the scope of +the inner `y`. The last `println!` produces `at the end: x = Some(5), y = 10`. + +### `let` Statements + +The `match` statement isn't the only place we can use patterns, though. For +example, consider this straightforward variable assignment with `let`: ```rust let x = 5; ``` -We've done this hundreds of times throughout this book. Well, you didn't -realize it, but you were using patterns! A `let` statement looks like this, -gramatically: +We've done this hundreds of times throughout this book. You may not have +realized it, but you were using patterns! A `let` statement looks like this, +more formally: ```text let PATTERN = EXPRESSION; @@ -29,45 +144,68 @@ let PATTERN = EXPRESSION; We've seen statements like `let x = 5;` with a variable name in the `PATTERN` slot; a variable name is just a particularly humble form of pattern. -With `let`, we compare the expression against the pattern, and assign any -names we find. So for example, in our `let x = 5;` case, `x` is a pattern -that says "bind what matches here to the variable `x`. And since that's the -whole pattern, it effecitvely means "bind everything to the variable `x`." +With `let`, we compare the expression against the pattern, and assign any names +we find. So for example, in our `let x = 5;` case, `x` is a pattern that says +"bind what matches here to the variable `x`. And since the name `x` is the +whole pattern, this pattern effecitvely means "bind everything to the variable +`x`, whatever the value is." -To see this a bit more clearly, consider this code: +To see the pattern matching aspect of `let` a bit more clearly, consider this +code: ```rust let (x, y, z) = (1, 2, 3); ``` Here, we have a tuple that we're matching against a pattern. Rust will compare -the value `(1, 2, 3)` to the pattern `(x, y, z)`, and see that it's valid. In -this case, it will bind `x` to `1`, `y` to `2`, and `z` to `3`. +the value `(1, 2, 3)` to the pattern `(x, y, z)`, and see that the value +matches the pattern. In this case, it will bind `1` to `x`, `2` to `y`, and `3` +to `z`. -We can mix and match and nest patterns: you can think of this tuple pattern as -nesting three individual variable patterns inside of it. Or something like: +We can mix, match, and nest patterns: you can think of this tuple pattern as +nesting three individual variable patterns inside of it. We can also do +something more complicated like this example where we nest tuples inside of +tuples: ```rust let ((one, two), name, (three, four), five) = ((1, 2), "hello", (3, 4), 5); ``` -Where we nest tons of things inside of each other. +Usually, Rust will warn you if you create a variable but don't use it anywhere, +since that could be a bug. If you're prototyping or just starting a project, +though, you might create a variable that you'll use eventually, but temporarily +it will be unused. If you're in this situation and would like to tell Rust not +to warn you about the unused variable, you can start the name of the variable +with an underscore. This works just like a variable name in any pattern, only +Rust won't warn you if the variable goes unused. In the following example, we +do get a warning about not using the variable `y`, but we don't get a warning +about not using the variable `_x`: -## Refutability +```rust +fn main() { + let _x = 5; + let y = 10; +} +``` -Patterns come in two forms: refutable, and irrefutable. Patterns which cannot -fail to match are "irrefutable", and patterns which can fail to match are said -to be "refutable". +`let` takes an irrefutable pattern. -Consider our `let x = 5;` example. `let` takes an irrefutable pattern, and this -is true for `x`: since it matches anything, it can't fail to match. Consider -trying to match an enum with `let`, something like +One place where patterns are required to be irrefutable is in `let` statements. +Consider our `let x = 5;` example. `x` is indeed an irrefutable pattern we're +allowed to use: since it matches anything, it can't fail to match. In contrast, +consider trying to match one variant of an enum with `let`, such as matching +only a `Some` value from the `Option` enum: ```rust,ignore let Some(x) = some_option_value; ``` -This can't work, and Rust will complain: +If `some_option_value` was the `None` value, `some_option_value` would not +match the pattern `Some(x)`. The pattern `Some(x)` is refutable since there +exists a case in which it would fail to match a value. There's nothing valid +that our code could do with this `let` statement if `some_option_value` was the +`None` value. Therefore, Rust will complain at compile time that we've tried to +use a refutable pattern where an irrefutable pattern is required: ```text error[E0005]: refutable pattern in local binding: `None` not covered @@ -77,10 +215,16 @@ error[E0005]: refutable pattern in local binding: `None` not covered | ^^^^^^^ pattern `None` not covered ``` -We didn't cover (and couldn't cover!) every valid option here, and so Rust will -rightfully complain. What should it have done with the `None` case? +We didn't cover (and couldn't cover!) every valid option with the pattern +`Some(x)`, so Rust will rightfully complain. + +### `if let` statements -On the other hand, `if let` takes a refutable pattern: +If we have a refutable pattern, instead of using `let`, we use `if let`. That +way, if the pattern doesn't match, the code inside the curly braces won't +execute. That code will only make sense and run if the value matches the +pattern. Here's our example with `Some(x)` matching `some_option_value` that is +allowed, since it uses `if let`: ```rust,ignore if let Some(x) = some_option_value { @@ -88,7 +232,8 @@ if let Some(x) = some_option_value { } ``` -And if you give it an irrefutable one... +Consequently, if we give `if let` an irrefutable pattern that will always match, +such as `x`: ```rust if let x = 5 { @@ -96,7 +241,8 @@ if let x = 5 { }; ``` -...it will complain: +Rust will complain that it doesn't make sense to use `if let` with an +irrefutable pattern: ```text error[E0162]: irrefutable if-let pattern @@ -106,74 +252,9 @@ error[E0162]: irrefutable if-let pattern | ^ irrefutable pattern ``` -In general, you shouldn't have to worry about this distinction; just be -familliar with the word when you see it, and realize that you need to change -either the pattern, or the construct you're using the pattern with. - -## Where can I use patterns? - -Patterns pop up in a number of places in Rust. You've been using them a lot -without realizing it! - -### `let` statements - -We talked about this above, the basic grammar of a `let` statement is: - -```text -let PATTERN = EXPRESSION; -``` - -Many people don't realize that you can use any patterns on the left hand side -of a `let` statement, not just binding patterns. You'll see more examples of -this later in this chapter. - -`let`, as mentioned above as well, takes an irrefutable pattern. - -### `match` arms - -Patterns are used very heavily by `match` expressions, particularly in match -arms. - -```text -match VALUE { - PATTERN => EXPRESSION, - PATTERN => EXPRESSION, - PATTERN => EXPRESSION, -} -``` - -These patterns are refuatble. However... +### Function Parameters -#### Exhaustiveness and `_` - -`match` expressions are required to be exhaustive. So when we put all of the -patterns together, they must cover all of our bases. One way to ensure you have -every possibility covered is to introduce an irrefutable pattern, like a -binding. Since it is irrefutable, it can never fail, and so covers every case -by definition. - -There's an additional irrefutable pattern that's often used in this case -though: `_`. It matches anything, but it never binds any variables. This can be -useful when you only want to do things for some patterns, but ignore the rest, -for example. - -#### Shadowing in patterns - -As with all variables, those declared by a pattern will shadow variables -outside of the `match` construct: - -```rust -let x = Some(5); - -match x { - Some(x) => { }, // x is an i32 here, not an Option - None => (), -} -``` - -### Function Arguments - -Similarly to `let`, function arguments are also irrefutable patterns: +Similarly to `let`, function parameters are also irrefutable patterns: ```rust fn foo(x: i32) { @@ -192,9 +273,18 @@ fn foo(_: i32) { Normally, you just wouldn't declare an argument, but maybe you're implementing a trait, or need a certain type signature for some other reason. This lets you -not have to use the argument. +not have to use the argument, and the compiler won't warn about unused function +parameters like it would if we had given it a name . + +### `while let` + +TODO: add a `while let` example -## Kinds of pattern +### `for` loops + +TODO: add a `for` loop example + +## All the Pattern Syntax Here's a list of all of the different types of patterns. @@ -233,7 +323,7 @@ match x { This prints `one or two`. -### ref and ref mut +### `ref` and `ref mut` Usually, when you match against a pattern, variables are bound to a value. This means you'll end up moving the value into the `match`: @@ -332,7 +422,9 @@ Or like this: let numbers = (2, 4, 8, 16, 32); match numbers { - (first, _, third, _, fifth) => println!("Some numbers: {}, {}, {}", first, third, fifth), + (first, _, third, _, fifth) => { + println!("Some numbers: {}, {}, {}", first, third, fifth) + }, } ``` @@ -403,8 +495,8 @@ match x { } ``` -This prints `no`, because the if applies to the whole of `4 | 5`, and not to only -the `5`. In other words, the precedence of if behaves like this: +This prints `no`, because the if applies to the whole of `4 | 5`, and not to +only the `5`. In other words, the precedence of if behaves like this: ```text (4 | 5) if y => ... @@ -416,7 +508,9 @@ not this: 4 | (5 if y) => ... ``` -### Bindings +### `@` Bindings + +TODO: difference between this and variable names You can bind values to names with `@`: @@ -435,3 +529,7 @@ match msg { In this case, we want to compare `id` against the range `3...7`, but we also want to save the actual value of `id`. + +## Summary + +TODO: summary From 51abccf6e6da047f18d5bd60352a4d1811cd7961 Mon Sep 17 00:00:00 2001 From: "Carol (Nichols || Goulding)" Date: Mon, 27 Feb 2017 14:25:34 -0500 Subject: [PATCH 03/22] spelling --- second-edition/dictionary.txt | 1 + second-edition/src/ch18-00-patterns.md | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/second-edition/dictionary.txt b/second-edition/dictionary.txt index 8c4349953b..43a471e226 100644 --- a/second-edition/dictionary.txt +++ b/second-edition/dictionary.txt @@ -232,6 +232,7 @@ refactoring refcell RefCell RefMut +Refutability repr retweet ripgrep diff --git a/second-edition/src/ch18-00-patterns.md b/second-edition/src/ch18-00-patterns.md index ecc81f6ac4..0f45cf25cb 100644 --- a/second-edition/src/ch18-00-patterns.md +++ b/second-edition/src/ch18-00-patterns.md @@ -28,7 +28,7 @@ possible failure and we wouldn't need their functionality if the pattern could never fail. In general, you shouldn't have to worry about the distinction between refutable -and irrefutable patterns; just be familliar with the concept of refutability +and irrefutable patterns; just be familiar with the concept of refutability when you see it mentioned in an error message. When you get an error message involving refutability, you'll need to change either the pattern or the construct you're using the pattern with, depending on your intentions for the @@ -147,7 +147,7 @@ slot; a variable name is just a particularly humble form of pattern. With `let`, we compare the expression against the pattern, and assign any names we find. So for example, in our `let x = 5;` case, `x` is a pattern that says "bind what matches here to the variable `x`. And since the name `x` is the -whole pattern, this pattern effecitvely means "bind everything to the variable +whole pattern, this pattern effectively means "bind everything to the variable `x`, whatever the value is." To see the pattern matching aspect of `let` a bit more clearly, consider this From e8864b28b9961066b8c3a38487c590b2d25467b7 Mon Sep 17 00:00:00 2001 From: "Carol (Nichols || Goulding)" Date: Mon, 27 Feb 2017 14:45:22 -0500 Subject: [PATCH 04/22] Fix code examples --- second-edition/src/ch18-00-patterns.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/second-edition/src/ch18-00-patterns.md b/second-edition/src/ch18-00-patterns.md index 0f45cf25cb..e03b378580 100644 --- a/second-edition/src/ch18-00-patterns.md +++ b/second-edition/src/ch18-00-patterns.md @@ -226,7 +226,8 @@ execute. That code will only make sense and run if the value matches the pattern. Here's our example with `Some(x)` matching `some_option_value` that is allowed, since it uses `if let`: -```rust,ignore +```rust +# let some_option_value: Option = None; if let Some(x) = some_option_value { println!("{}", x); } @@ -235,7 +236,7 @@ if let Some(x) = some_option_value { Consequently, if we give `if let` an irrefutable pattern that will always match, such as `x`: -```rust +```rust,ignore if let x = 5 { println!("{}", x); }; From 4b4cd225822f9eff90257e5e025cff7f9c48cea2 Mon Sep 17 00:00:00 2001 From: "Carol (Nichols || Goulding)" Date: Mon, 27 Feb 2017 14:58:29 -0500 Subject: [PATCH 05/22] Let's try refutability this way --- second-edition/dictionary.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/second-edition/dictionary.txt b/second-edition/dictionary.txt index 43a471e226..5eac1f1922 100644 --- a/second-edition/dictionary.txt +++ b/second-edition/dictionary.txt @@ -232,7 +232,7 @@ refactoring refcell RefCell RefMut -Refutability +refutability repr retweet ripgrep From adbd028e9d0e0141364f49d80a32f8709ae0569c Mon Sep 17 00:00:00 2001 From: "Carol (Nichols || Goulding)" Date: Mon, 27 Feb 2017 15:44:08 -0500 Subject: [PATCH 06/22] More rearranging, notes with TODOs --- second-edition/src/ch18-00-patterns.md | 350 +++++++++++++++---------- 1 file changed, 214 insertions(+), 136 deletions(-) diff --git a/second-edition/src/ch18-00-patterns.md b/second-edition/src/ch18-00-patterns.md index e03b378580..98aaa23cee 100644 --- a/second-edition/src/ch18-00-patterns.md +++ b/second-edition/src/ch18-00-patterns.md @@ -3,39 +3,16 @@ Patterns are a special syntax within Rust for matching against the structure of our types, complex or simple. We take some value and compare it against the pattern. If the pattern matches our value, then we do something with the value -parts. Recall chapter 6 when we discussed the `match` expression that uses +parts. Recall in Chapter 6 when we discussed the `match` expression that uses patterns like a coin sorting machine: patterns describe the "shape" of the data we're working with. We can name pieces within the shape, like we named the state that appeared on quarters in Chapter 6, and if the data fits the shape, we can use the named pieces. This chapter is a reference on all things related to patterns. We'll cover the -difference between *refutable* and *irrefutable* patterns, the valid places to -use patterns, and the different kinds of pattern syntax that you might see and -what it means. - -## Refutability: Whether a Pattern Might Fail to Match - -Patterns come in two forms: refutable and irrefutable. Patterns which cannot -fail to match for any possible value are *irrefutable*, and patterns which can -fail to match for some possible value are said to be *refutable*. Some places -where you can use patterns, like `let` statements (which we'll get into more in -the next section in this chapter), are restricted to only accept irrefutable -patterns, since there's nothing correct the program could do if the pattern -fails to match. Other places you can use patterns, like `if let` expressions, -are restricted to only accept refutable patterns, since they're made to handle -possible failure and we wouldn't need their functionality if the pattern could -never fail. - -In general, you shouldn't have to worry about the distinction between refutable -and irrefutable patterns; just be familiar with the concept of refutability -when you see it mentioned in an error message. When you get an error message -involving refutability, you'll need to change either the pattern or the -construct you're using the pattern with, depending on your intentions for the -behavior of the code. - -Let's now go through all the different places we can use patterns, and we'll -mention whether each place takes a refutable or irrefutable pattern as we do so. +valid places to use patterns, the difference between *refutable* and +*irrefutable* patterns, and the different kinds of pattern syntax that you +might see. ## All the Places Patterns May be Used @@ -58,28 +35,20 @@ match VALUE { } ``` -Generally, match arms use refutable patterns. A `match` with only one arm whose -pattern is irrefutable is allowed, but it's not particularly useful and could -be replaced with a simpler `let` statement. There's one caveat with patterns -and `match` expressions, however: the set of all patterns in a `match` must be -*exhaustive* and cover all possible cases. - -#### Exhaustiveness and `_` +#### Exhaustiveness and the Default Pattern `_` `match` expressions are required to be exhaustive. When we put all of the patterns in the arms together, all possibilities for the value in the `match` expression must be accounted for. One way to ensure you have every possibility -covered is to have an irrefutable pattern for the last arm, like a variable -name. Since a name matching any value is an irrefutable pattern, it can never -fail and thus covers every case remaining after the previous arms' patterns by -definition. +covered is to have a catch-all pattern for the last arm, like a variable name. +A name matching any value can never fail and thus covers every case remaining +after the previous arms' patterns. -There's an additional irrefutable pattern that's often used in the last match -arm: `_`. It matches anything, but it never binds any variables. This can be -useful when you only want to do things for some patterns, but ignore the rest, -for example. +There's an additional pattern that's often used in the last match arm: `_`. It +matches anything, but it never binds any variables. This can be useful when you +only want to run code for some patterns but ignore the rest, for example. -#### Shadowing in patterns +#### Shadowing in Patterns As with all variables, those declared by a pattern will shadow variables outside of the `match` construct since a `match` starts a new scope. In the @@ -124,9 +93,19 @@ None`. Once the `match` expression is over, its scope ends, and so does the scope of the inner `y`. The last `println!` produces `at the end: x = Some(5), y = 10`. +### `if let` Expressions + +We also discussed `if let` expressions in Chapter 6, and how they're mostly a +shorter way to write the equivalent of a `match` that only cares about matching +one case. `if let` can optionally have a corresponding `else` with code to run +if the pattern in the `if let` doesn't match. + +TODO: `if let` example that isn't too repetitive with ch06-03 + ### `let` Statements -The `match` statement isn't the only place we can use patterns, though. For +`match` and `if let` are the places we've explicitly discussed using patterns +earlier in the book, but they aren't the only places we've *used* patterns. For example, consider this straightforward variable assignment with `let`: ```rust @@ -151,7 +130,7 @@ whole pattern, this pattern effectively means "bind everything to the variable `x`, whatever the value is." To see the pattern matching aspect of `let` a bit more clearly, consider this -code: +code where we're destructuring a tuple: ```rust let (x, y, z) = (1, 2, 3); @@ -160,52 +139,81 @@ let (x, y, z) = (1, 2, 3); Here, we have a tuple that we're matching against a pattern. Rust will compare the value `(1, 2, 3)` to the pattern `(x, y, z)`, and see that the value matches the pattern. In this case, it will bind `1` to `x`, `2` to `y`, and `3` -to `z`. +to `z`. You can think of this tuple pattern as nesting three individual +variable patterns inside of it. -We can mix, match, and nest patterns: you can think of this tuple pattern as -nesting three individual variable patterns inside of it. We can also do -something more complicated like this example where we nest tuples inside of -tuples: +### Function Parameters + +Similarly to `let`, function parameters can also be patterns. This code +declaring a function named `foo` that takes one parameter named `x` of type +`i32` should look familiar: ```rust -let ((one, two), name, (three, four), five) = ((1, 2), "hello", (3, 4), 5); +fn foo(x: i32) { + // code goes here +} ``` -Usually, Rust will warn you if you create a variable but don't use it anywhere, -since that could be a bug. If you're prototyping or just starting a project, -though, you might create a variable that you'll use eventually, but temporarily -it will be unused. If you're in this situation and would like to tell Rust not -to warn you about the unused variable, you can start the name of the variable -with an underscore. This works just like a variable name in any pattern, only -Rust won't warn you if the variable goes unused. In the following example, we -do get a warning about not using the variable `y`, but we don't get a warning -about not using the variable `_x`: +The `x` part is a pattern! In a similar way as we did with `let`, we could +match a tuple in a function's arguments. This example shows how we could split +apart the values in a tuple as part of passing the tuple to a function: + +``` +fn print_coordinates(&(x, y): &(i32, i32)) { + println!("Current location: ({}, {})", x, y); +} -```rust fn main() { - let _x = 5; - let y = 10; + let point = (3, 5); + print_coordinates(&point); } ``` -`let` takes an irrefutable pattern. +This will print `Current location: (3, 5)`. + +### `while let` + +TODO: add a `while let` example + +### `for` loops + +TODO: add a `for` loop example + +## Refutability: Whether a Pattern Might Fail to Match + +Patterns come in two forms: refutable and irrefutable. Patterns which cannot +fail to match for any possible value are *irrefutable*, and patterns which can +fail to match for some possible value are said to be *refutable*. `let` +statements, function parameters, and `for` loops are restricted to only accept +irrefutable patterns, since there's nothing correct the program could do if the +pattern fails to match. `match`, `if let`, and `while let` expressions are +restricted to only accept refutable patterns, since they're made to handle +possible failure and we wouldn't need their functionality if the pattern could +never fail. + +In general, you shouldn't have to worry about the distinction between refutable +and irrefutable patterns; just be familiar with the concept of refutability +when you see it mentioned in an error message. When you get an error message +involving refutability, you'll need to change either the pattern or the +construct you're using the pattern with, depending on your intentions for the +behavior of the code. -One place where patterns are required to be irrefutable is in `let` statements. -Consider our `let x = 5;` example. `x` is indeed an irrefutable pattern we're -allowed to use: since it matches anything, it can't fail to match. In contrast, -consider trying to match one variant of an enum with `let`, such as matching -only a `Some` value from the `Option` enum: +Let's look at some examples. Earlier in this chapter, we had `let x = 5;`. `x` +is indeed an irrefutable pattern we're allowed to use: since it matches +anything, it can't fail to match. In contrast, consider trying to match one +variant of an enum with `let`, such as matching only a `Some` value from the +`Option` enum: ```rust,ignore let Some(x) = some_option_value; ``` -If `some_option_value` was the `None` value, `some_option_value` would not -match the pattern `Some(x)`. The pattern `Some(x)` is refutable since there -exists a case in which it would fail to match a value. There's nothing valid -that our code could do with this `let` statement if `some_option_value` was the -`None` value. Therefore, Rust will complain at compile time that we've tried to -use a refutable pattern where an irrefutable pattern is required: +If `some_option_value` was a `None` value, `some_option_value` would not match +the pattern `Some(x)`. The pattern `Some(x)` is refutable since there exists a +case in which it would fail to match a value. There's nothing valid that our +code could do with this `let` statement if `some_option_value` was the `None` +value. Therefore, Rust will complain at compile time that we've tried to use a +refutable pattern where an irrefutable pattern is required: ```text error[E0005]: refutable pattern in local binding: `None` not covered @@ -218,8 +226,6 @@ error[E0005]: refutable pattern in local binding: `None` not covered We didn't cover (and couldn't cover!) every valid option with the pattern `Some(x)`, so Rust will rightfully complain. -### `if let` statements - If we have a refutable pattern, instead of using `let`, we use `if let`. That way, if the pattern doesn't match, the code inside the curly braces won't execute. That code will only make sense and run if the value matches the @@ -253,45 +259,17 @@ error[E0162]: irrefutable if-let pattern | ^ irrefutable pattern ``` -### Function Parameters - -Similarly to `let`, function parameters are also irrefutable patterns: - -```rust -fn foo(x: i32) { - // code goes here -} -``` - -The `x` part is a pattern! If we didn't want to use the argument in the body of -our function, we could use `_` for example: - -```rust -fn foo(_: i32) { - // code goes here -} -``` - -Normally, you just wouldn't declare an argument, but maybe you're implementing -a trait, or need a certain type signature for some other reason. This lets you -not have to use the argument, and the compiler won't warn about unused function -parameters like it would if we had given it a name . - -### `while let` - -TODO: add a `while let` example - -### `for` loops - -TODO: add a `for` loop example +Generally, match arms use refutable patterns. A `match` with only one arm whose +pattern is irrefutable is allowed, but it's not particularly useful and could +be replaced with a simpler `let` statement. ## All the Pattern Syntax Here's a list of all of the different types of patterns. -### Literals & _ +### Literals -You can match against literals directly, and `_` acts as an ‘any’ case: +You can match against literals directly ```rust let x = 1; @@ -306,7 +284,45 @@ match x { This prints `one`. -Literals are refutable patterns, but `_` is irrefutable. +TODO: would you consider references with `&` as a sort of literal? or is that +more like destructuring? + +### Named Variables and Underscore + +TODO: variable name always matches any value, underscore ignores everything + +Usually, Rust will warn you if you create a variable but don't use it anywhere, +since that could be a bug. If you're prototyping or just starting a project, +though, you might create a variable that you'll use eventually, but temporarily +it will be unused. If you're in this situation and would like to tell Rust not +to warn you about the unused variable, you can start the name of the variable +with an underscore. This works just like a variable name in any pattern, only +Rust won't warn you if the variable goes unused. In the following example, we +do get a warning about not using the variable `y`, but we don't get a warning +about not using the variable `_x`: + +```rust +fn main() { + let _x = 5; + let y = 10; +} +``` + +Similarly with patterns as function parameters, if we didn't want to use the +argument in the body of our function, we could use `_` for example: + +```rust +fn foo(_: i32) { + // code goes here +} +``` + +Normally, you just wouldn't declare an argument, but maybe you're implementing +a trait, or need a certain type signature for some other reason. This lets you +not have to use the argument, and the compiler won't warn about unused function +parameters like it would if we had given it a name. + +TODO: names starting with underscores behave the same as names not starting with underscores, aside from not getting an unused warning. Underscore is special; it drops right away. Example? ### Multiple patterns @@ -324,10 +340,11 @@ match x { This prints `one or two`. -### `ref` and `ref mut` +### `ref` and `ref mut` to Create References in Patterns Usually, when you match against a pattern, variables are bound to a value. -This means you'll end up moving the value into the `match`: +This means you'll end up moving the value into the `match` since the ownership +rules apply: ```rust,ignore let name = Some(String::from("Bors")); @@ -341,7 +358,9 @@ match name { println!("name is: {:?}", name); ``` -If you'd prefer to bind `name` to a reference, use the `ref` keyword: +Because using `&` in a pattern matches an existing reference in the value, if +you want to create a reference and borrow a value in a name, use the `ref` +keyword: ```rust let name = Some(String::from("Bors")); @@ -351,12 +370,12 @@ match name { None => (), } -// name is not moved here; the match only took a reference to its data rather -// than moving it. This will work: println!("name is: {:?}", name); ``` -And for a mutable reference, `ref mut`: +This example will compile because `name` is not moved into the `Some(ref name)` arm of the match; the match only took a reference to the data in `name` rather than moving it. + +To create a mutable reference, use `ref mut` for the same reason: ```rust let mut name = Some(String::from("Bors")); @@ -366,14 +385,15 @@ match name { None => (), } -// name is not moved here; the match only took a reference to its data rather -// than moving it println!("name is: {:?}", name); ``` -### Destructuring +### Destructuring to Break Apart Values -Patterns can be used to destructure structs and enums: +Patterns can be used to *destructure* structs, enums, and tuples. Destructuring +means to break a value up into its component pieces. This example shows a +`Point` struct with two fields, `x` and `y`, that we can break apart by using a +pattern with a `let` statement: ```rust struct Point { @@ -381,14 +401,17 @@ struct Point { y: i32, } -let origin = Point { x: 0, y: 0 }; +fn main() { + let p = Point { x: 0, y: 7 }; -let Point { x, y } = origin; + let Point { x, y } = p; + assert_eq!(0, x); + assert_eq!(7, y); +} ``` -This brings `x` and `y` variables into scope, matching the `x` and `y` of -`origin`. While it can be unusual in `let`, this is the same principle of -patterns in `match`: +This creates the variables `x` and `y` that match the `x` and `y` of +`p`. The names of the variables must match the names of the fields to use this shorthand. If we wanted to use names different than the variable names, we can specify `field_name: variable_name` in the pattern. In this example, `a` will have the value in the `Point` instance's `x` field and `b` will have the value in the `y` field: ```rust struct Point { @@ -396,17 +419,30 @@ struct Point { y: i32, } -let origin = Point { x: 0, y: 0 }; +fn main() { + let p = Point { x: 0, y: 7 }; -match origin { - Point { x, y } => { }, // variables x and y are created here + let Point { x: a, y: b } = p; + assert_eq!(0, a); + assert_eq!(7, b); } ``` -### Ignoring values +We can mix, match, and nest destructuring patterns: we can do +something complicated like this example where we nest tuples inside of +tuples: + +TODO: nest structs inside of tuples inside of enums or something + +```rust +let ((one, two), name, (three, four), five) = ((1, 2), "hello", (3, 4), 5); +``` + +### Ignoring Values with Nested Underscores or `..` -We discussed using `_` as a whole pattern to ignore it above, but you can -also use `_` inside of another pattern to ignore just part of it: +We discussed using `_` as a whole pattern to ignore it in the "Named Variables +and Underscore" section, but you can also use `_` inside of another pattern to +ignore just part of it: ```rust let x = Some(5); @@ -445,7 +481,45 @@ match origin { } ``` -### Ranges +TODO: explain this example with `..` in the middle that works: + +```rust +fn main() { + let numbers = (2, 4, 8, 16, 32); + + match numbers { + (first, .., fifth) => { + println!("Some numbers: {}, {}", first, fifth) + }, + } +} +``` + +TODO: explain this example with `..` twice that doesn't work because it's ambiguous: + +```rust,ignore +fn main() { + let numbers = (2, 4, 8, 16, 32); + + match numbers { + (.., second, ..) => { + println!("Some numbers: {}", second) + }, + } +} +``` + +error: + +```text +error: `..` can only be used once per tuple or tuple struct pattern + --> src/main.rs:5:22 + | +5 | (.., second, ..) => { + | ^^ +``` + +### Matching Ranges of Values with `...` You can match a range of values with `...`: @@ -470,9 +544,11 @@ match x { } ``` -### Guards +### Extra Conditionals with Match Guards -You can introduce ‘match guards’ with `if`: +TODO: is this really part of pattern syntax? + +You can introduce *match guards* with `if`: ```rust let x = Some(5); @@ -484,7 +560,7 @@ match x { } ``` -If you’re using if with multiple patterns, the if applies to both sides: +If you're using if with multiple patterns, the if applies to both sides: ```rust let x = 4; @@ -531,6 +607,8 @@ match msg { In this case, we want to compare `id` against the range `3...7`, but we also want to save the actual value of `id`. +TODO: consult source code to make sure we covered all pattern syntax + ## Summary TODO: summary From 03323abe77ae4ef851e517e818d7698c2a40722c Mon Sep 17 00:00:00 2001 From: "Carol (Nichols || Goulding)" Date: Mon, 27 Feb 2017 15:54:14 -0500 Subject: [PATCH 07/22] Resolve comments, leave more TODOs --- second-edition/src/ch18-00-patterns.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/second-edition/src/ch18-00-patterns.md b/second-edition/src/ch18-00-patterns.md index 98aaa23cee..63145bd48d 100644 --- a/second-edition/src/ch18-00-patterns.md +++ b/second-edition/src/ch18-00-patterns.md @@ -269,7 +269,7 @@ Here's a list of all of the different types of patterns. ### Literals -You can match against literals directly +You can match against literals directly: ```rust let x = 1; @@ -284,10 +284,12 @@ match x { This prints `one`. +TODO: but not floating point literals right? + TODO: would you consider references with `&` as a sort of literal? or is that more like destructuring? -### Named Variables and Underscore +### Named Variables and the Underscore Pattern TODO: variable name always matches any value, underscore ignores everything @@ -428,6 +430,8 @@ fn main() { } ``` +TODO: add enum destructuring example + We can mix, match, and nest destructuring patterns: we can do something complicated like this example where we nest tuples inside of tuples: From b453fa24f96942b3848db39499cf1e77a19e6ec6 Mon Sep 17 00:00:00 2001 From: "Carol (Nichols || Goulding)" Date: Mon, 27 Feb 2017 16:06:43 -0500 Subject: [PATCH 08/22] Clarify range is ONLY good for char & numeric --- second-edition/src/ch18-00-patterns.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/second-edition/src/ch18-00-patterns.md b/second-edition/src/ch18-00-patterns.md index 63145bd48d..1cd219e211 100644 --- a/second-edition/src/ch18-00-patterns.md +++ b/second-edition/src/ch18-00-patterns.md @@ -536,7 +536,8 @@ match x { } ``` -Ranges are usually used with integers or `char`s: +Ranges are only allowed with numeric values or `char` values. Here's an example +using ranges of `char` values: ```rust let x = 'c'; @@ -548,6 +549,8 @@ match x { } ``` +This will print `early ASCII letter`. + ### Extra Conditionals with Match Guards TODO: is this really part of pattern syntax? From e97a64231b3adf30e9cba0a468f330deaccbd3fa Mon Sep 17 00:00:00 2001 From: "Carol (Nichols || Goulding)" Date: Mon, 27 Feb 2017 17:22:20 -0500 Subject: [PATCH 09/22] add another note --- second-edition/src/ch18-00-patterns.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/second-edition/src/ch18-00-patterns.md b/second-edition/src/ch18-00-patterns.md index 1cd219e211..bee6dfa893 100644 --- a/second-edition/src/ch18-00-patterns.md +++ b/second-edition/src/ch18-00-patterns.md @@ -291,7 +291,7 @@ more like destructuring? ### Named Variables and the Underscore Pattern -TODO: variable name always matches any value, underscore ignores everything +TODO: variable name always matches any value, underscore ignores everything, say the word "wildcard" somewhere about `_` Usually, Rust will warn you if you create a variable but don't use it anywhere, since that could be a bug. If you're prototyping or just starting a project, From 2f56ea987285931ec2728d89ea3f070fcde2e5b2 Mon Sep 17 00:00:00 2001 From: "Carol (Nichols || Goulding)" Date: Mon, 27 Feb 2017 20:48:40 -0500 Subject: [PATCH 10/22] more words --- second-edition/dictionary.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/second-edition/dictionary.txt b/second-edition/dictionary.txt index 5eac1f1922..fdab46a492 100644 --- a/second-edition/dictionary.txt +++ b/second-edition/dictionary.txt @@ -329,6 +329,7 @@ wasn WeatherForecast WebSocket whitespace +wildcard wildcards workflow workspace From cebc14efe60b248fdb68cd58cb194e796d341120 Mon Sep 17 00:00:00 2001 From: steveklabnik Date: Sat, 1 Apr 2017 11:45:12 -0400 Subject: [PATCH 11/22] fix some TODOs --- second-edition/src/ch18-00-patterns.md | 110 ++++++++++++++++++++++--- 1 file changed, 97 insertions(+), 13 deletions(-) diff --git a/second-edition/src/ch18-00-patterns.md b/second-edition/src/ch18-00-patterns.md index bee6dfa893..c71e7f796f 100644 --- a/second-edition/src/ch18-00-patterns.md +++ b/second-edition/src/ch18-00-patterns.md @@ -173,11 +173,50 @@ This will print `Current location: (3, 5)`. ### `while let` -TODO: add a `while let` example +A similar construction to `if let` is `while let`; this allows you to do +a `while` loop while matching a pattern. For example: + +```rust +let mut stack = Vec::new(); + +stack.push(1); +stack.push(2); +stack.push(3); + +while let Some(top) = stack.pop() { + // Prints 3, 2, 1 + println!("{}", top); +} +``` + +Here, the `pop` method takes the last element out of the vector and returns `Some(value)`. +If the vector is empty, it returns `None`. We can use `while let` to pop every element +off this way. ### `for` loops -TODO: add a `for` loop example +Looping with `for`, as we discussed in Chapter 3, is the most common loop construction in +Rust code. But what we didn't talk about in that chapter was that `for` takes a pattern. +For example, the `enumerate` method adapts an iterator to not only produce a value, but +a tuple with the value and its index. We can use a pattern to destructure that tuple: + +```rust +let v = vec![1, 2, 3]; + +for (index, value) in v { + println!("{} is at index {}", value, index); +} +``` + +This will print: + +```text +1 is at index 0 +2 is at index 1 +3 is at index 2 +``` + +The `(index, value)` construction there is a pattern. ## Refutability: Whether a Pattern Might Fail to Match @@ -291,8 +330,6 @@ more like destructuring? ### Named Variables and the Underscore Pattern -TODO: variable name always matches any value, underscore ignores everything, say the word "wildcard" somewhere about `_` - Usually, Rust will warn you if you create a variable but don't use it anywhere, since that could be a bug. If you're prototyping or just starting a project, though, you might create a variable that you'll use eventually, but temporarily @@ -311,7 +348,8 @@ fn main() { ``` Similarly with patterns as function parameters, if we didn't want to use the -argument in the body of our function, we could use `_` for example: +argument in the body of our function, we could use `_`, also known as a +'wildcard' for example: ```rust fn foo(_: i32) { @@ -326,6 +364,32 @@ parameters like it would if we had given it a name. TODO: names starting with underscores behave the same as names not starting with underscores, aside from not getting an unused warning. Underscore is special; it drops right away. Example? +There's one more bit of subtlety here; `_x` and `_` have different behavior. That +is, `_x` still binds to the variable of that name, but `_` doesn't bind at all. + +For example, this will produce an error: + +```rust,ignore +let s = String::from("Hello!"); + +let _s = s; + +println!("{}", s); +``` + +Specifically, "use of moved value". When we assign `s` to `_s`, it moves, and so +`s` is no longer valid. However... + +```rust +let s = String::from("Hello!"); + +let _ = s; + +println!("{}", s); +``` + +This works just fine. Becuase we never bind `s` to anything, it's not moved. + ### Multiple patterns You can match multiple patterns with `|`: @@ -430,7 +494,22 @@ fn main() { } ``` -TODO: add enum destructuring example +It's also common to use destructuring with `enums`. For example, the `is_none` method +on `Option` returns `true` if the `Option` is `None`, and false if it is `Some`. +Here's what it looks like: + +```rust,ignore +impl Option { + pub fn is_some(&self) -> bool { + match *self { + Some(_) => true, + None => false, + } + } +} +``` + +The `Some(_)` pattern will match any value, as long as it's `Some`. We can mix, match, and nest destructuring patterns: we can do something complicated like this example where we nest tuples inside of @@ -485,21 +564,24 @@ match origin { } ``` -TODO: explain this example with `..` in the middle that works: +`..` will expand to as many values as it needs to be. For example: ```rust fn main() { let numbers = (2, 4, 8, 16, 32); match numbers { - (first, .., fifth) => { - println!("Some numbers: {}, {}", first, fifth) + (first, .., last) => { + println!("Some numbers: {}, {}", first, last); }, } } ``` -TODO: explain this example with `..` twice that doesn't work because it's ambiguous: +Here, we have the first and last value matched, with `first` and `last`. The `..` will +match all of the things in the middle. + +This doesn't always work, though. Consider this: ```rust,ignore fn main() { @@ -513,7 +595,7 @@ fn main() { } ``` -error: +We get this error: ```text error: `..` can only be used once per tuple or tuple struct pattern @@ -523,6 +605,10 @@ error: `..` can only be used once per tuple or tuple struct pattern | ^^ ``` +It's not possible to determine that this is _actually_ the second value, like we gave +the name. What if both `..` matched two elements? It'd be the third, value, not the second. +Since this is ambiguous, Rust disallows it. + ### Matching Ranges of Values with `...` You can match a range of values with `...`: @@ -553,8 +639,6 @@ This will print `early ASCII letter`. ### Extra Conditionals with Match Guards -TODO: is this really part of pattern syntax? - You can introduce *match guards* with `if`: ```rust From 4639ff234c91b29c9dedfa268422c3ce8d7a1f10 Mon Sep 17 00:00:00 2001 From: "Carol (Nichols || Goulding)" Date: Fri, 7 Apr 2017 10:24:10 -0400 Subject: [PATCH 12/22] Fix typo --- second-edition/src/ch18-00-patterns.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/second-edition/src/ch18-00-patterns.md b/second-edition/src/ch18-00-patterns.md index c71e7f796f..00a34e6d8a 100644 --- a/second-edition/src/ch18-00-patterns.md +++ b/second-edition/src/ch18-00-patterns.md @@ -388,7 +388,7 @@ let _ = s; println!("{}", s); ``` -This works just fine. Becuase we never bind `s` to anything, it's not moved. +This works just fine. Because we never bind `s` to anything, it's not moved. ### Multiple patterns From 68eda186fb22359d30d53479bac72d0434d3d94c Mon Sep 17 00:00:00 2001 From: "Carol (Nichols || Goulding)" Date: Fri, 7 Apr 2017 15:59:35 -0400 Subject: [PATCH 13/22] Rearranging and editing --- second-edition/src/ch18-00-patterns.md | 848 +++++++++++++++++-------- 1 file changed, 580 insertions(+), 268 deletions(-) diff --git a/second-edition/src/ch18-00-patterns.md b/second-edition/src/ch18-00-patterns.md index 00a34e6d8a..da579dbf1f 100644 --- a/second-edition/src/ch18-00-patterns.md +++ b/second-edition/src/ch18-00-patterns.md @@ -24,8 +24,8 @@ patterns are valid. As we discussed in Chapter 6, a common place patterns are used is in the arms of `match` expressions. Formally, `match` expressions are defined as the -keyword `match`, a value, and one or more arms that consist of a pattern and an -expression to run if the value matches that arm's pattern: +keyword `match`, a value to match on, and one or more match arms that consist +of a pattern and an expression to run if the value matches that arm's pattern: ```text match VALUE { @@ -46,61 +46,130 @@ after the previous arms' patterns. There's an additional pattern that's often used in the last match arm: `_`. It matches anything, but it never binds any variables. This can be useful when you -only want to run code for some patterns but ignore the rest, for example. +only want to run code for some patterns but ignore any other value, for example. -#### Shadowing in Patterns +### `if let` Expressions + +We discussed `if let` expressions in Chapter 6, and how they're mostly a +shorter way to write the equivalent of a `match` that only cares about matching +one case. `if let` can optionally have a corresponding `else` with code to run +if the pattern in the `if let` doesn't match. -As with all variables, those declared by a pattern will shadow variables -outside of the `match` construct since a `match` starts a new scope. In the -next example, we declare a variable named `x` with the value `Some(5)` and a -variable `y` with the value `10`. Then we have a `match` expression on the -value `x`. Take a look at the patterns in the match arms and the `println!` at -the end, and make a guess about what will be printed before running this code -or reading further: +Listing 18-1 shows that it's even possible to mix and match `if let`, `else +if`, and `else if let`. This code shows a series of checks of a bunch of +different conditions to decide what the background color should be. For the +purposes of the examplee, we've created variables with hardcoded values that a +real program might get by asking the user. If the user has specified a favorite +color, we'll use that as the background color. If today is Tuesday, the +background color will be green. If the user has specified their age as a string +and we can parse it as a number successfully, we'll use either purple or orange +depending on the value of the parsed number. Finally, if none of these +conditions apply, the background color will be blue: + +Filename: src/main.rs ```rust fn main() { - 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), + let favorite_color: Option<&str> = None; + let is_tuesday = false; + let age: Result = "34".parse(); + + if let Some(color) = favorite_color { + println!("Using your favorite color, {}, as the background", color); + } else if is_tuesday { + println!("Tuesday is green day!"); + } else if let Ok(age) = age { + if age > 30 { + println!("Using purple as the background color"); + } else { + println!("Using orange as the background color"); + } + } else { + println!("Using blue as the background color"); } +} +``` - println!("at the end: x = {:?}, y = {:?}", x, y); +Listing 18-1: Mixing `if let`, `else if`, `else if let`, +and `else` + +This conditional structure lets us support complex requirements. With the +hardcoded values we have here, this example will print `Using purple as the +background color`. + +Note that `if let` can also introduce shadowed variables like `match` arms can: +`if let Ok(age) = age` introduces a new shadowed `age` variable that contains +the value inside the `Ok` variant. This also means the `if age > 30` condition +needs to go within the block; we aren't able to combine these two conditions +into `if let Ok(age) = age && age > 30` since the shadowed `age` that we want +to compare to 30 isn't valid until the new scope starts with the curly brace. + +Also note that conditionals with many cases like these are not as powerful as +`match` expression since exhaustiveness is not checked by the compiler. If we +leave off the last `else` block and miss handling some cases, the compiler will +not error. This example might be too complex to rewrite as a readable `match`, +so we should take extra care to check that we're handling all the cases since +the compiler is not checking exhaustiveness for us. + +### `while let` + +A similar construction to `if let` is `while let`: this allows you to do a +`while` loop as long as a pattern continues to match. Listing 18-2 shows an +example using a `while let` loop to use a vector as a stack and print out the +values in the vector in the opposite order that we pushed the values in: + +```rust +let mut stack = Vec::new(); + +stack.push(1); +stack.push(2); +stack.push(3); + +while let Some(top) = stack.pop() { + println!("{}", top); } ``` -The first match arm has the pattern `Some(50)`, and the value in `x` -(`Some(5)`) does not match `Some(50)`, so we continue. In the second match arm, -the pattern `Some(y)` introduces a new variable name `y` that will match any -value inside a `Some` value. Because we're in a new scope inside the `match` -expression, this is a new variable, not the `y` we declared at the beginning -that has the value 10. So the new `y` binding will match any value inside a -`Some`, which is what we have in `x`, so we execute the expression for that arm -and print `Matched, y = 5` since this `y` binds to the inner value of the -`Some` in `x`, which is 5. +Listing 18-2: Using a `while let` loop to print out values +as long as `stack.pop()` returns `Some` -If `x` had been a `None` value instead of `Some(5)`, we would have matched the -underscore since the other two arms' patterns would not have matched. In the -expression for that match arm, since we did not introduce an `x` variable in -the pattern of the arm, this `x` is still the outer `x` that has not been -shadowed. In this hypothetical case, the `match` would print `Default case, x = -None`. +This example will print 3, 2, then 1. The `pop` method takes the last element +out of the vector and returns `Some(value)`. If the vector is empty, it returns +`None`. The `while` loop will continue running the code in its block as long as +`pop` is returning `Some`. Once it returns `None`, the `while` loop stops. We +can use `while let` to pop every element off our stack. -Once the `match` expression is over, its scope ends, and so does the scope of -the inner `y`. The last `println!` produces `at the end: x = Some(5), y = 10`. +### `for` loops -### `if let` Expressions +Looping with `for`, as we discussed in Chapter 3, is the most common loop +construction in Rust code. What we didn't talk about in that chapter was that +`for` takes a pattern. In Listing 18-3, we're demonstrating how we can use a +pattern in a `for` loop to destructure a tuple. The `enumerate` method adapts +an iterator to produce a value and the index of the value in the iterator in a +tuple: -We also discussed `if let` expressions in Chapter 6, and how they're mostly a -shorter way to write the equivalent of a `match` that only cares about matching -one case. `if let` can optionally have a corresponding `else` with code to run -if the pattern in the `if let` doesn't match. +```rust +let v = vec![1, 2, 3]; + +for (index, value) in v.iter().enumerate() { + println!("{} is at index {}", value, index); +} +``` + +Listing 18-3: Using a pattern in a `for` loop to +destructure the tuple returned from `enumerate` into its pieces + +This will print: + +```text +1 is at index 0 +2 is at index 1 +3 is at index 2 +``` -TODO: `if let` example that isn't too repetitive with ch06-03 +The first call to `enumerate` produces the tuple `(0, 1)`. When this value is +matched to the pattern `(index, value)`, `index` will be 0 and `value` will +equal 1. ### `let` Statements @@ -129,24 +198,31 @@ we find. So for example, in our `let x = 5;` case, `x` is a pattern that says whole pattern, this pattern effectively means "bind everything to the variable `x`, whatever the value is." -To see the pattern matching aspect of `let` a bit more clearly, consider this -code where we're destructuring a tuple: +To see the pattern matching aspect of `let` a bit more clearly, consider +Listing 18-4 where we're using a pattern with `let` to destructuring a tuple: ```rust let (x, y, z) = (1, 2, 3); ``` +Listing 18-4: Using a pattern to destructure a tuple and +create 3 variables at once + Here, we have a tuple that we're matching against a pattern. Rust will compare -the value `(1, 2, 3)` to the pattern `(x, y, z)`, and see that the value -matches the pattern. In this case, it will bind `1` to `x`, `2` to `y`, and `3` -to `z`. You can think of this tuple pattern as nesting three individual -variable patterns inside of it. +the value `(1, 2, 3)` to the pattern `(x, y, z)` and see that the value matches +the pattern. In this case, it will bind `1` to `x`, `2` to `y`, and `3` to `z`. +You can think of this tuple pattern as nesting three individual variable +patterns inside of it. + +We saw another example of destructuring a tuple in Chapter 16, Listing 16-6, +where we destructured the return value of `mpsc::channel()` into the `tx` +(transmitter) and `rx` (receiver) parts. ### Function Parameters -Similarly to `let`, function parameters can also be patterns. This code -declaring a function named `foo` that takes one parameter named `x` of type -`i32` should look familiar: +Similarly to `let`, function parameters can also be patterns. The code in +Listing 18-5 declaring a function named `foo` that takes one parameter named +`x` of type `i32` should look familiar: ```rust fn foo(x: i32) { @@ -154,11 +230,16 @@ fn foo(x: i32) { } ``` +Listing 18-5: A function signature uses patterns in the +parameters + The `x` part is a pattern! In a similar way as we did with `let`, we could -match a tuple in a function's arguments. This example shows how we could split +match a tuple in a function's arguments. Listing 18-6 shows how we could split apart the values in a tuple as part of passing the tuple to a function: -``` +Filename: src/main.rs + +```rust fn print_coordinates(&(x, y): &(i32, i32)) { println!("Current location: ({}, {})", x, y); } @@ -169,54 +250,19 @@ fn main() { } ``` -This will print `Current location: (3, 5)`. - -### `while let` - -A similar construction to `if let` is `while let`; this allows you to do -a `while` loop while matching a pattern. For example: +Listing 18-6: A function with parameters that destructure +a tuple -```rust -let mut stack = Vec::new(); - -stack.push(1); -stack.push(2); -stack.push(3); +This will print `Current location: (3, 5)`. When we pass the value `&(3, 5)` to +`print_coordinates`, the values match the pattern `&(x, y)`. `x` gets the value +3, and `y` gets the value 5. -while let Some(top) = stack.pop() { - // Prints 3, 2, 1 - println!("{}", top); -} -``` +Because closures are similar to functions, as we discussed in Chapter 13, we +can use patterns in closure parameter lists as well. -Here, the `pop` method takes the last element out of the vector and returns `Some(value)`. -If the vector is empty, it returns `None`. We can use `while let` to pop every element -off this way. - -### `for` loops - -Looping with `for`, as we discussed in Chapter 3, is the most common loop construction in -Rust code. But what we didn't talk about in that chapter was that `for` takes a pattern. -For example, the `enumerate` method adapts an iterator to not only produce a value, but -a tuple with the value and its index. We can use a pattern to destructure that tuple: - -```rust -let v = vec![1, 2, 3]; - -for (index, value) in v { - println!("{} is at index {}", value, index); -} -``` - -This will print: - -```text -1 is at index 0 -2 is at index 1 -3 is at index 2 -``` - -The `(index, value)` construction there is a pattern. +One difference between the places we can use patterns is that with `for` loops, +`let`, and in function parameters, the patterns must be *irrefutable*. Let's +discuss that next. ## Refutability: Whether a Pattern Might Fail to Match @@ -241,12 +287,15 @@ Let's look at some examples. Earlier in this chapter, we had `let x = 5;`. `x` is indeed an irrefutable pattern we're allowed to use: since it matches anything, it can't fail to match. In contrast, consider trying to match one variant of an enum with `let`, such as matching only a `Some` value from the -`Option` enum: +`Option` enum as shown in Listing 18-7: ```rust,ignore let Some(x) = some_option_value; ``` +Listing 18-7: Attempting to use a refutable pattern with +`let` + If `some_option_value` was a `None` value, `some_option_value` would not match the pattern `Some(x)`. The pattern `Some(x)` is refutable since there exists a case in which it would fail to match a value. There's nothing valid that our @@ -262,14 +311,15 @@ error[E0005]: refutable pattern in local binding: `None` not covered | ^^^^^^^ pattern `None` not covered ``` -We didn't cover (and couldn't cover!) every valid option with the pattern +We didn't cover (and couldn't cover!) every valid value with the pattern `Some(x)`, so Rust will rightfully complain. -If we have a refutable pattern, instead of using `let`, we use `if let`. That -way, if the pattern doesn't match, the code inside the curly braces won't +If we have a refutable pattern, instead of using `let`, we can use `if let`. +That way, if the pattern doesn't match, the code inside the curly braces won't execute. That code will only make sense and run if the value matches the -pattern. Here's our example with `Some(x)` matching `some_option_value` that is -allowed, since it uses `if let`: +pattern. Listing 18-8 shows how to fix the code in Listing 18-7 with `Some(x)` +matching `some_option_value`. Using the refutable pattern `Some(x)` is allowed, +since this example uses `if let`: ```rust # let some_option_value: Option = None; @@ -278,8 +328,11 @@ if let Some(x) = some_option_value { } ``` +Listing 18-8: Using `if let` and a block with refutable +patterns instead of `let` + Consequently, if we give `if let` an irrefutable pattern that will always match, -such as `x`: +such as `x` as shown in Listing 18-9: ```rust,ignore if let x = 5 { @@ -287,6 +340,9 @@ if let x = 5 { }; ``` +Listing 18-9: Attempting to use an irrefutable pattern +with `if let` + Rust will complain that it doesn't make sense to use `if let` with an irrefutable pattern: @@ -298,17 +354,24 @@ error[E0162]: irrefutable if-let pattern | ^ irrefutable pattern ``` -Generally, match arms use refutable patterns. A `match` with only one arm whose -pattern is irrefutable is allowed, but it's not particularly useful and could -be replaced with a simpler `let` statement. +Generally, match arms use refutable patterns, except for the last arm that +might match any remaining values with an irrefutable pattern. A `match` with +only one arm whose pattern is irrefutable is allowed, but it's not particularly +useful and could be replaced with a simpler `let` statement. + +Now that we've discussed all the places that patterns can be used and the +difference between refutable and irrefutable patterns, let's go over all the +syntax we can use to create patterns. ## All the Pattern Syntax -Here's a list of all of the different types of patterns. +We've seen some examples of different kinds of patterns throughout the book. +This section lists all the syntax valid in patterns and why you might want to +use each of them. ### Literals -You can match against literals directly: +As we saw in Chapter 6, you can match against literals directly: ```rust let x = 1; @@ -321,78 +384,68 @@ match x { } ``` -This prints `one`. +This prints `one` since the value in `x` is 1. -TODO: but not floating point literals right? +### Named Variables -TODO: would you consider references with `&` as a sort of literal? or is that -more like destructuring? +Named variables are irrefutable patterns that match any value. -### Named Variables and the Underscore Pattern +As with all variables, variables declared as part of a pattern will shadow +variables with the same name outside of the `match` construct since a `match` +starts a new scope. In Listing 18-10, we declare a variable named `x` with the +value `Some(5)` and a variable `y` with the value `10`. Then we have a `match` +expression on the value `x`. Take a look at the patterns in the match arms and +the `println!` at the end, and make a guess about what will be printed before +running this code or reading further: -Usually, Rust will warn you if you create a variable but don't use it anywhere, -since that could be a bug. If you're prototyping or just starting a project, -though, you might create a variable that you'll use eventually, but temporarily -it will be unused. If you're in this situation and would like to tell Rust not -to warn you about the unused variable, you can start the name of the variable -with an underscore. This works just like a variable name in any pattern, only -Rust won't warn you if the variable goes unused. In the following example, we -do get a warning about not using the variable `y`, but we don't get a warning -about not using the variable `_x`: +Filename: src/main.rs ```rust fn main() { - let _x = 5; + let x = Some(5); let y = 10; -} -``` -Similarly with patterns as function parameters, if we didn't want to use the -argument in the body of our function, we could use `_`, also known as a -'wildcard' for example: + match x { + Some(50) => println!("Got 50"), + Some(y) => println!("Matched, y = {:?}", y), + _ => println!("Default case, x = {:?}", x), + } -```rust -fn foo(_: i32) { - // code goes here + println!("at the end: x = {:?}, y = {:?}", x, y); } ``` -Normally, you just wouldn't declare an argument, but maybe you're implementing -a trait, or need a certain type signature for some other reason. This lets you -not have to use the argument, and the compiler won't warn about unused function -parameters like it would if we had given it a name. - -TODO: names starting with underscores behave the same as names not starting with underscores, aside from not getting an unused warning. Underscore is special; it drops right away. Example? - -There's one more bit of subtlety here; `_x` and `_` have different behavior. That -is, `_x` still binds to the variable of that name, but `_` doesn't bind at all. - -For example, this will produce an error: - -```rust,ignore -let s = String::from("Hello!"); - -let _s = s; - -println!("{}", s); -``` - -Specifically, "use of moved value". When we assign `s` to `_s`, it moves, and so -`s` is no longer valid. However... +Listing 18-10: A `match` statement with an arm that +introduces a shadowed variable `y` -```rust -let s = String::from("Hello!"); +Let's walk through what happens when the `match` statement runs. The first +match arm has the pattern `Some(50)`, and the value in `x` (`Some(5)`) does not +match `Some(50)`, so we continue. In the second match arm, the pattern +`Some(y)` introduces a new variable name `y` that will match any value inside a +`Some` value. Because we're in a new scope inside the `match` expression, this +is a new variable, not the `y` we declared at the beginning that has the value +10. The new `y` binding will match any value inside a `Some`, which is what we +have in `x`, so we execute the expression for that arm and print `Matched, y = +5` since this `y` binds to the inner value of the `Some` in `x`, which is 5. -let _ = s; +If `x` had been a `None` value instead of `Some(5)`, we would have matched the +underscore since the other two arms' patterns would not have matched. In the +expression for that match arm, since we did not introduce an `x` variable in +the pattern of the arm, this `x` is still the outer `x` that has not been +shadowed. In this hypothetical case, the `match` would print `Default case, x = +None`. -println!("{}", s); -``` +Once the `match` expression is over, its scope ends, and so does the scope of +the inner `y`. The last `println!` produces `at the end: x = Some(5), y = 10`. -This works just fine. Because we never bind `s` to anything, it's not moved. +In order to make a `match` expression that compares the values of the outer `x` +and `y` rather than introducing a shadowed variable, we would need to use a +match guard conditional instead. We'll be talking about match guards later in +this section. ### Multiple patterns -You can match multiple patterns with `|`: +You can match multiple patterns with `|`, which means *or*: ```rust let x = 1; @@ -406,60 +459,42 @@ match x { This prints `one or two`. -### `ref` and `ref mut` to Create References in Patterns - -Usually, when you match against a pattern, variables are bound to a value. -This means you'll end up moving the value into the `match` since the ownership -rules apply: - -```rust,ignore -let name = Some(String::from("Bors")); - -match name { - Some(name) => println!("Found a name: {}", name), - None => (), -} - -// name is moved here. This line will fail to compile: -println!("name is: {:?}", name); -``` +### Matching Ranges of Values with `...` -Because using `&` in a pattern matches an existing reference in the value, if -you want to create a reference and borrow a value in a name, use the `ref` -keyword: +You can match a range of values with `...`: ```rust -let name = Some(String::from("Bors")); +let x = 5; -match name { - Some(ref name) => println!("Found a name: {}", name), - None => (), +match x { + 1 ... 5 => println!("one through five"), + _ => println!("something else"), } - -println!("name is: {:?}", name); ``` -This example will compile because `name` is not moved into the `Some(ref name)` arm of the match; the match only took a reference to the data in `name` rather than moving it. - -To create a mutable reference, use `ref mut` for the same reason: +Ranges are only allowed with numeric values or `char` values. Here's an example +using ranges of `char` values: ```rust -let mut name = Some(String::from("Bors")); +let x = 'c'; -match name { - Some(ref mut name) => *name = String::from("Another name"), - None => (), +match x { + 'a' ... 'j' => println!("early ASCII letter"), + 'k' ... 'z' => println!("late ASCII letter"), + _ => println!("something else"), } - -println!("name is: {:?}", name); ``` +This will print `early ASCII letter`. + ### Destructuring to Break Apart Values -Patterns can be used to *destructure* structs, enums, and tuples. Destructuring -means to break a value up into its component pieces. This example shows a -`Point` struct with two fields, `x` and `y`, that we can break apart by using a -pattern with a `let` statement: +Patterns can be used to *destructure* structs, enums, tuples, and references. +Destructuring means to break a value up into its component pieces. Listing +18-15 shows a `Point` struct with two fields, `x` and `y`, that we can break +apart by using a pattern with a `let` statement: + +Filename: src/main.rs ```rust struct Point { @@ -476,8 +511,17 @@ fn main() { } ``` -This creates the variables `x` and `y` that match the `x` and `y` of -`p`. The names of the variables must match the names of the fields to use this shorthand. If we wanted to use names different than the variable names, we can specify `field_name: variable_name` in the pattern. In this example, `a` will have the value in the `Point` instance's `x` field and `b` will have the value in the `y` field: +Listing 18-15: Destructuring using struct field +shorthand + +This creates the variables `x` and `y` that match the `x` and `y` of `p`. The +names of the variables must match the names of the fields to use this +shorthand. If we wanted to use names different than the variable names, we can +specify `field_name: variable_name` in the pattern. In Listing 18-16, `a` will +have the value in the `Point` instance's `x` field and `b` will have the value +in the `y` field: + +Filename: src/main.rs ```rust struct Point { @@ -494,38 +538,107 @@ fn main() { } ``` -It's also common to use destructuring with `enums`. For example, the `is_none` method -on `Option` returns `true` if the `Option` is `None`, and false if it is `Some`. -Here's what it looks like: +Listing 18-16: Destructuring struct fields into variables +with different names than the fields -```rust,ignore -impl Option { - pub fn is_some(&self) -> bool { - match *self { - Some(_) => true, - None => false, - } - } -} +We used desctructuring on enums in Chapter 6, such as in Listing 6-5 where we +destructured an `Option` using a `match` expression and added one to the +inner value of the `Some` variant. + +When the value we're matching against a pattern contains a reference, we can +specify a `&` in the pattern in order to separate the reference and the value. +This is especially useful in closures used with iterators that iterate over +references to values when we want to use the values in the closure rather than +the references. Listing 18-17 shows how to iterate over references to `Point` +instances in a vector, and destructure both the reference and the struct in +order to be able to perform calculations on the `x` and `y` values easily: + +```rust +# struct Point { +# x: i32, +# y: i32, +# } +# +let points = vec![ + Point { x: 0, y: 0}, + Point { x: 1, y: 5}, + Point { x: 10, y: -3}, +]; +let sum_of_squares: i32 = points + .iter() + .map(|&Point {x, y}| x * x + y * y) + .sum(); +``` + +Listing 18-17: Destructuring a reference to a struct into +the struct field values + +Because `iter` iterates over references to the items in the vector, if we +forgot the `&` in the closure arguments in the `map`, we'd get a type mismatch +error like this: + +```text +error[E0308]: mismatched types + --> + | +14 | .map(|Point {x, y}| x * x + y * y) + | ^^^^^^^^^^^^ expected &Point, found struct `Point` + | + = note: expected type `&Point` + found type `Point` +``` + +This says Rust was expecting our closure to match `&Point`, but we tried to +match the value with a pattern that was a `Point` value, not a reference to a +`Point`. + +We can mix, match, and nest destructuring patterns in even more complex ways: +we can do something complicated like this example where we nest structs and and +tuples inside of a tuple and destructure all the primitive values out: + +```rust +# struct Point { +# x: i32, +# y: i32, +# } +# +let ((feet, inches), Point {x, y}) = ((3, 10), Point { x: 3, y: -10 }); ``` -The `Some(_)` pattern will match any value, as long as it's `Some`. +This lets us break complex types into their component parts. -We can mix, match, and nest destructuring patterns: we can do -something complicated like this example where we nest tuples inside of -tuples: +### Ignoring Values in a Pattern -TODO: nest structs inside of tuples inside of enums or something +There are a few ways to ignore entire values or parts of values: using the `_` +pattern, using the `_` pattern within another pattern, using a name that starts +with an underscore, or using `..` to ignore all remaining parts of a value. +Let's explore how and why to do each of these. + +#### Ignoring an Entire Value with `_` + +We've seen the use of underscore as a wildcard pattern that will match any value +but not bind to the value. While the underscore pattern is especially useful as +the last arm in a `match` expression, we can use it in any pattern, such as +function arguments as shown in Listing 18-11: ```rust -let ((one, two), name, (three, four), five) = ((1, 2), "hello", (3, 4), 5); +fn foo(_: i32) { + // code goes here +} ``` -### Ignoring Values with Nested Underscores or `..` +Listing 18-11: Using `_` in a function signature -We discussed using `_` as a whole pattern to ignore it in the "Named Variables -and Underscore" section, but you can also use `_` inside of another pattern to -ignore just part of it: +Normally, you would change the signature to not have the unused parameter. In +cases such as implementing a trait, where you need a certain type signature, +using an underscore lets you ignore a parameter, and the compiler won't warn +about unused function parameters like it would if we had used a name instead. + +#### Ignoring Parts of a Value with a Nested `_` + +We can also use `_` inside of another pattern to ignore just part of a value. +In Listing 18-12, the first `match` arm's pattern matches a `Some` value but +ignores the value inside of the `Some` variant as specified by the underscore: ```rust let x = Some(5); @@ -536,7 +649,15 @@ match x { } ``` -Or like this: +Listing 18-12: Ignoring the value inside of the `Some` +variant by using a nested underscore + +This is useful when the code associated with the `match` arm doesn't use the +nested part of the variable at all. + +We can also use underscores in multiple places within one pattern, as shown in +Listing 18-13 where we're ignoring the second and fourth values in a tuple of +five items: ```rust let numbers = (2, 4, 8, 16, 32); @@ -548,7 +669,76 @@ match numbers { } ``` -If you want, you can use `..` to ignore all of the parts you haven't defined: +Listing 18-13: Ignoring multiple parts of a tuple + +This will print `Some numbers: 2, 8, 32`, and the values 4 and 16 will be +ignored. + +#### Ignoring an Unused Variable by Starting its Name with an Underscore + +Usually, Rust will warn you if you create a variable but don't use it anywhere, +since that could be a bug. If you're prototyping or just starting a project, +though, you might create a variable that you'll use eventually, but temporarily +it will be unused. If you're in this situation and would like to tell Rust not +to warn you about the unused variable, you can start the name of the variable +with an underscore. This works just like a variable name in any pattern, only +Rust won't warn you if the variable goes unused. In Listing 18-12, we +do get a warning about not using the variable `y`, but we don't get a warning +about not using the variable `_x`: + +```rust +fn main() { + let _x = 5; + let y = 10; +} +``` + +Listing 18-12: Starting a variable name with an underscore +in order to not get unused variable warnings + +Note that there is a subtle difference between using only `_` and using a name +that starts with an underscore like `_x`: `_x` still binds the value to the +variable, but `_` doesn't bind at all. + +Listing 18-13 shows a case where this distinction matters: `s` will still be +moved into `_s`, which prevents us from using `s` again: + +```rust,ignore +let s = String::from("Hello!"); + +let _s = s; + +println!("{}", s); +``` + +Listing 18-13: An unused variable starting with an +underscore still binds the value, which may take ownership of the value + +Using underscore by itself, however, doesn't ever bind to the value. Listing +18-14 will compile without any errors since `s` does not get moved into `_`: + +```rust +let s = String::from("Hello!"); + +let _ = s; + +println!("{}", s); +``` + +Listing 18-14: Using underscore does not bind the +value + +This works just fine. Because we never bind `s` to anything, it's not moved. + +### Ignoring Remaining Parts of a Value with `..` + +With values that have many parts, we can extract only a few parts and avoid +having to list underscores for each remaining part by instead using `..`. The +`..` pattern will ignore any parts of a value that we haven't explicitly +matched in the rest of the pattern. In Listing 18-15, we have a `Point` struct +that holds a coordinate in three dimensional space. In the `match` expression, +we only want to operate on the `x` coordinate and ignore the values in the `y` +and `z` fields: ```rust struct Point { @@ -560,11 +750,19 @@ struct Point { let origin = Point { x: 0, y: 0, z: 0 }; match origin { - Point { x, .. } => { }, // y and z are ignored + Point { x, .. } => println!("x is {}", x), } ``` -`..` will expand to as many values as it needs to be. For example: +Listing 18-15: Ignoring all fields of a `Point` except +for `x` by using `..` + +Using `..` is shorter to type than having to list out `_y` and `_z`. The `..` +pattern is especially useful when working with structs that have lots of fields +in situations where only one or two fields are relevant. + +`..` will expand to as many values as it needs to be. Listing 18-16 shows a use +of `..` with a tuple: ```rust fn main() { @@ -578,10 +776,15 @@ fn main() { } ``` -Here, we have the first and last value matched, with `first` and `last`. The `..` will -match all of the things in the middle. +Listing 18-16: Matching only the first and last values in +a tuple and ignoring all other values with `..` -This doesn't always work, though. Consider this: +Here, we have the first and last value matched, with `first` and `last`. The +`..` will match and ignore all of the things in the middle. + +Using `..` must be unambiguous, however. Listing 18-17 shows an example where +it's not clear to Rust which values we want to match and which values we want +to ignore: ```rust,ignore fn main() { @@ -595,7 +798,10 @@ fn main() { } ``` -We get this error: +Listing 18-17: An attempt to use `..` in a way that is +ambiguous + +If we compile this example, we get this error: ```text error: `..` can only be used once per tuple or tuple struct pattern @@ -605,82 +811,173 @@ error: `..` can only be used once per tuple or tuple struct pattern | ^^ ``` -It's not possible to determine that this is _actually_ the second value, like we gave -the name. What if both `..` matched two elements? It'd be the third, value, not the second. -Since this is ambiguous, Rust disallows it. +It's not possible to determine how many values in the tuple should be ignored +before one value is matched with `second`, and then how many further values are +ignored after that. We could mean that we want to ignore 2, bind `second` to 4, +then ignore 8, 16, and 32, or we could mean that we want to ignore 2 and 4, +bind `second` to 8, then ignore 16 and 32, and so forth. The variable name +`second` doesn't mean anything special to Rust, so we get a compiler error +since using `..` in two places like this is ambiguous. -### Matching Ranges of Values with `...` +### `ref` and `ref mut` to Create References in Patterns -You can match a range of values with `...`: +Usually, when you match against a pattern, the variables that the pattern +introduces are bound to a value. This means you'll end up moving the value into +the `match` (or wherever you're using the pattern) since the ownership rules +apply. Listing 18-18 shows an example: + +```rust,ignore +let robot_name = Some(String::from("Bors")); + +match robot_name { + Some(name) => println!("Found a name: {}", name), + None => (), +} + +println!("robot_name is: {:?}", robot_name); +``` + +Listing 18-18: Creating a variable in a match arm pattern +takes ownership of the value + +This example will fail to compile since the value inside the `Some` value in +`robot_name` is moved within the `match` when `name` binds to that value. + +Using `&` in a pattern matches an existing reference in the value, as we saw in +the previous section on destructuring. If you want to create a reference +instead in order to borrow the value in a pattern variable, use the `ref` +keyword before the new variable, as shown in Listing 18-19: ```rust -let x = 5; +let robot_name = Some(String::from("Bors")); -match x { - 1 ... 5 => println!("one through five"), - _ => println!("something else"), +match robot_name { + Some(ref name) => println!("Found a name: {}", name), + None => (), } + +println!("robot_name is: {:?}", robot_name); ``` -Ranges are only allowed with numeric values or `char` values. Here's an example -using ranges of `char` values: +Listing 18-19: Creating a reference so that a pattern +variable does not take ownership of a value + +This example will compile because the value in the `Some` variant in +`robot_name` is not moved into the `Some(ref name)` arm of the match; the match +only took a reference to the data in `robot_name` rather than moving it. + +To create a mutable reference, use `ref mut` for the same reason as shown in +Listing 18-20: ```rust -let x = 'c'; +let mut robot_name = Some(String::from("Bors")); -match x { - 'a' ... 'j' => println!("early ASCII letter"), - 'k' ... 'z' => println!("late ASCII letter"), - _ => println!("something else"), +match robot_name { + Some(ref mut name) => *name = String::from("Another name"), + None => (), } + +println!("robot_name is: {:?}", robot_name); ``` -This will print `early ASCII letter`. +Listing 18-20: Creating a mutable reference to a value as +part of a pattern using `ref mut` + +This example will compile and print `robot_name is: Some("Another name")`. +Since `name` is a mutable reference, within the match arm code, we need to +dereference using the `*` operator in order to be able to mutate the value. ### Extra Conditionals with Match Guards -You can introduce *match guards* with `if`: +You can introduce *match guards* as part of a match arm by specifying an +additional `if` conditional after the pattern. The conditional can use +variables created in the pattern. Listing 18-21 has a `match` expression with a +match guard in the first arm: ```rust -let x = Some(5); +let num = Some(4); -match x { +match num { Some(x) if x < 5 => println!("less than five: {}", x), Some(x) => println!("{}", x), None => (), } ``` -If you're using if with multiple patterns, the if applies to both sides: +Listing 18-21: Adding a match guard to a pattern + +This example will print `less than five: 4`. If `num` was instead `Some(7)`, +this example would print `7`. Match guards allow you to express more complexity +than patterns alone give you. + +In Listing 18-10, we saw that since patterns shadow variables, we weren't able +to specify a pattern to express the case when a value was equal to a variable +outside the `match`. Listing 18-22 shows how we can use a match guard to +accomplish this: + +```rust +fn main() { + let x = Some(5); + let y = 10; + + match x { + Some(50) => println!("Got 50"), + Some(n) if n == y => println!("Matched, n = {:?}", n), + _ => println!("Default case, x = {:?}", x), + } + + println!("at the end: x = {:?}, y = {:?}", x, y); +} +``` + +Listing 18-22: Using a match guard to test for equality +with an outer variable + +This will now print `Default case, x = Some(5)`. Because the second match arm +is not introducing a new variable `y` that shadows the outer `y` in the +pattern, we can use `y` in the match guard. We're still destructuring `x` to +get the inner value `n`, and then we can compare `n` and `y` in the match guard. + + +If you're using a match guard with multiple patterns specified by `|`, the +match guard condition applies to all of the patterns. Listing 18-23 shows a +match guard that applies to the value matched by all three patterns in the +first arm: ```rust let x = 4; let y = false; match x { - 4 | 5 if y => println!("yes"), + 4 | 5 | 6 if y => println!("yes"), _ => println!("no"), } ``` -This prints `no`, because the if applies to the whole of `4 | 5`, and not to -only the `5`. In other words, the precedence of if behaves like this: +Listing 18-23: Combining multiple patterns with a match +guard + +This prints `no` since the `if` condition applies to the whole pattern `4 | 5 | +6`, not only to the last value `6`. In other words, the precedence of a match +guard in relation to a pattern behaves like this: ```text -(4 | 5) if y => ... +(4 | 5 | 6) if y => ... ``` -not this: +rather than this: ```text -4 | (5 if y) => ... +4 | 5 | (6 if y) => ... ``` ### `@` Bindings -TODO: difference between this and variable names - -You can bind values to names with `@`: +In order to test a value in a pattern but also be able to create a variable +bound to the value, we can use `@`. Listing 18-24 shows an example where we +want to test that a `Message::Hello` `id` field is within the range `3...7` but +also be able to bind to the value so that we can use it in the code associated +with the arm: ```rust enum Message { @@ -690,15 +987,30 @@ enum Message { let msg = Message::Hello { id: 5 }; match msg { - Message::Hello { id: id @ 3...7 } => println!("{}", id), - _ => (), + Message::Hello { id: id @ 3...7 } => { + println!("Found an id in range: {}", id) + }, + Message::Hello { id: 10...12 } => { + println!("Found an id in another range") + }, + Message::Hello { id } => { + println!("Found some other id: {}", id) + }, } ``` -In this case, we want to compare `id` against the range `3...7`, but we also -want to save the actual value of `id`. +Listing 18-24: Using `@` to bind to a value in a pattern +while also testing it -TODO: consult source code to make sure we covered all pattern syntax +This example will print `Found an id in range: 5`. By specifying `id @` before +the range, we're capturing whatever value matched the range while also testing +it. In the second arm where we only have a range specified in the pattern, the +code associated with the arm doesn't know if `id` is 10, 11, or 12, since we +haven't saved the `id` value in a variable: we only know that the value matched +something in that range if that arm's code is executed. In the last arm where +we've specified a variable without a range, we do have the value available to +use in the arm's code, but we haven't applied any other test to the value. +Using `@` lets us test a value and save it in a variable within one pattern. ## Summary From 2f779401fd47625292e82dfa8221d5a163dfd418 Mon Sep 17 00:00:00 2001 From: "Carol (Nichols || Goulding)" Date: Fri, 7 Apr 2017 16:05:24 -0400 Subject: [PATCH 14/22] Add an example of matching on a struct field value Fixes #554. --- second-edition/src/ch18-00-patterns.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/second-edition/src/ch18-00-patterns.md b/second-edition/src/ch18-00-patterns.md index da579dbf1f..ee56e9a868 100644 --- a/second-edition/src/ch18-00-patterns.md +++ b/second-edition/src/ch18-00-patterns.md @@ -541,6 +541,30 @@ fn main() { Listing 18-16: Destructuring struct fields into variables with different names than the fields +We can also use destructuring with literal values in order to test and use inner parts of a value. Listing 18-17 shows a `match` statement that determines whether a point lies directly on the `x` axis (which is true when `y = 0`), on the `y` axis (`x = 0`), or neither: + +``` +# struct Point { +# x: i32, +# y: i32, +# } +# +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), + } +} +``` + +Listing 18-17: Destructuring and matching literal values in one pattern + +This will print `On the y axis at 7` since the value `p` matches the second arm +by virtue of `x` having the value 0. + We used desctructuring on enums in Chapter 6, such as in Listing 6-5 where we destructured an `Option` using a `match` expression and added one to the inner value of the `Some` variant. From f2bb785135b1574aa5fad6bd1a53d112714181e2 Mon Sep 17 00:00:00 2001 From: "Carol (Nichols || Goulding)" Date: Fri, 7 Apr 2017 16:12:33 -0400 Subject: [PATCH 15/22] Clarify in the intro a bit better what a pattern is Fixes #280. --- second-edition/src/ch18-00-patterns.md | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/second-edition/src/ch18-00-patterns.md b/second-edition/src/ch18-00-patterns.md index ee56e9a868..29975b3f22 100644 --- a/second-edition/src/ch18-00-patterns.md +++ b/second-edition/src/ch18-00-patterns.md @@ -1,11 +1,15 @@ # Patterns Match the Structure of Values Patterns are a special syntax within Rust for matching against the structure of -our types, complex or simple. We take some value and compare it against the -pattern. If the pattern matches our value, then we do something with the value -parts. Recall in Chapter 6 when we discussed the `match` expression that uses -patterns like a coin sorting machine: patterns describe the "shape" of the data -we're working with. We can name pieces within the shape, like we named the +our types, complex or simple. A pattern is made up of some combination of +literals; destructured arrays, enums, structs, or tuples; variables, wildcards, +and placeholders. These pieces describe the "shape" of the data we're working +with. + +We use a pattern by taking some value and comparing it against the pattern. If +the pattern matches our value, we do something with the value parts. Recall in +Chapter 6 when we discussed the `match` expression that uses patterns like a +coin sorting machine. We can name pieces within the shape, like we named the state that appeared on quarters in Chapter 6, and if the data fits the shape, we can use the named pieces. From b0abb6469ae567002f53829fee445d5a8b3d256b Mon Sep 17 00:00:00 2001 From: "Carol (Nichols || Goulding)" Date: Fri, 7 Apr 2017 18:37:03 -0400 Subject: [PATCH 16/22] Wrapping lines --- second-edition/src/ch18-00-patterns.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/second-edition/src/ch18-00-patterns.md b/second-edition/src/ch18-00-patterns.md index 29975b3f22..d7980c8db6 100644 --- a/second-edition/src/ch18-00-patterns.md +++ b/second-edition/src/ch18-00-patterns.md @@ -545,7 +545,10 @@ fn main() { Listing 18-16: Destructuring struct fields into variables with different names than the fields -We can also use destructuring with literal values in order to test and use inner parts of a value. Listing 18-17 shows a `match` statement that determines whether a point lies directly on the `x` axis (which is true when `y = 0`), on the `y` axis (`x = 0`), or neither: +We can also use destructuring with literal values in order to test and use +inner parts of a value. Listing 18-17 shows a `match` statement that determines +whether a point lies directly on the `x` axis (which is true when `y = 0`), on +the `y` axis (`x = 0`), or neither: ``` # struct Point { @@ -564,7 +567,8 @@ fn main() { } ``` -Listing 18-17: Destructuring and matching literal values in one pattern +Listing 18-17: Destructuring and matching literal values +in one pattern This will print `On the y axis at 7` since the value `p` matches the second arm by virtue of `x` having the value 0. From 6ae549962df611412822d9a767179518b1e2c7ca Mon Sep 17 00:00:00 2001 From: "Carol (Nichols || Goulding)" Date: Fri, 7 Apr 2017 19:11:22 -0400 Subject: [PATCH 17/22] Add a summary --- second-edition/src/ch18-00-patterns.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/second-edition/src/ch18-00-patterns.md b/second-edition/src/ch18-00-patterns.md index d7980c8db6..c715154759 100644 --- a/second-edition/src/ch18-00-patterns.md +++ b/second-edition/src/ch18-00-patterns.md @@ -1046,4 +1046,12 @@ Using `@` lets us test a value and save it in a variable within one pattern. ## Summary -TODO: summary +Patterns are a useful feature of Rust that help to distinguish between +different kinds of data. When used in `match` statements, Rust makes sure that +your patterns cover every possible value. Patterns in `let` statements and +function parameters make those constructs more powerful, enabling the +destructuring of values into smaller parts at the same time as assigning to +variables. + +Now, for the penultimate chapter of the book, let's take a look at some +advanced parts of a variety of Rust's features. From b9213801d015c419935c0cb83cc8a442fc32faff Mon Sep 17 00:00:00 2001 From: "Carol (Nichols || Goulding)" Date: Fri, 7 Apr 2017 19:18:10 -0400 Subject: [PATCH 18/22] Fix listing numbers --- second-edition/src/ch18-00-patterns.md | 83 +++++++++++++------------- 1 file changed, 41 insertions(+), 42 deletions(-) diff --git a/second-edition/src/ch18-00-patterns.md b/second-edition/src/ch18-00-patterns.md index c715154759..470e96548a 100644 --- a/second-edition/src/ch18-00-patterns.md +++ b/second-edition/src/ch18-00-patterns.md @@ -495,7 +495,7 @@ This will print `early ASCII letter`. Patterns can be used to *destructure* structs, enums, tuples, and references. Destructuring means to break a value up into its component pieces. Listing -18-15 shows a `Point` struct with two fields, `x` and `y`, that we can break +18-11 shows a `Point` struct with two fields, `x` and `y`, that we can break apart by using a pattern with a `let` statement: Filename: src/main.rs @@ -515,13 +515,13 @@ fn main() { } ``` -Listing 18-15: Destructuring using struct field +Listing 18-11: Destructuring using struct field shorthand This creates the variables `x` and `y` that match the `x` and `y` of `p`. The names of the variables must match the names of the fields to use this shorthand. If we wanted to use names different than the variable names, we can -specify `field_name: variable_name` in the pattern. In Listing 18-16, `a` will +specify `field_name: variable_name` in the pattern. In Listing 18-12, `a` will have the value in the `Point` instance's `x` field and `b` will have the value in the `y` field: @@ -542,15 +542,15 @@ fn main() { } ``` -Listing 18-16: Destructuring struct fields into variables +Listing 18-12: Destructuring struct fields into variables with different names than the fields We can also use destructuring with literal values in order to test and use -inner parts of a value. Listing 18-17 shows a `match` statement that determines +inner parts of a value. Listing 18-13 shows a `match` statement that determines whether a point lies directly on the `x` axis (which is true when `y = 0`), on the `y` axis (`x = 0`), or neither: -``` +```rust # struct Point { # x: i32, # y: i32, @@ -567,7 +567,7 @@ fn main() { } ``` -Listing 18-17: Destructuring and matching literal values +Listing 18-13: Destructuring and matching literal values in one pattern This will print `On the y axis at 7` since the value `p` matches the second arm @@ -581,7 +581,7 @@ When the value we're matching against a pattern contains a reference, we can specify a `&` in the pattern in order to separate the reference and the value. This is especially useful in closures used with iterators that iterate over references to values when we want to use the values in the closure rather than -the references. Listing 18-17 shows how to iterate over references to `Point` +the references. Listing 18-14 shows how to iterate over references to `Point` instances in a vector, and destructure both the reference and the struct in order to be able to perform calculations on the `x` and `y` values easily: @@ -602,7 +602,7 @@ let sum_of_squares: i32 = points .sum(); ``` -Listing 18-17: Destructuring a reference to a struct into +Listing 18-14: Destructuring a reference to a struct into the struct field values Because `iter` iterates over references to the items in the vector, if we @@ -651,7 +651,7 @@ Let's explore how and why to do each of these. We've seen the use of underscore as a wildcard pattern that will match any value but not bind to the value. While the underscore pattern is especially useful as the last arm in a `match` expression, we can use it in any pattern, such as -function arguments as shown in Listing 18-11: +function arguments as shown in Listing 18-15: ```rust fn foo(_: i32) { @@ -659,7 +659,7 @@ fn foo(_: i32) { } ``` -Listing 18-11: Using `_` in a function signature +Listing 18-15: Using `_` in a function signature Normally, you would change the signature to not have the unused parameter. In cases such as implementing a trait, where you need a certain type signature, @@ -669,7 +669,7 @@ about unused function parameters like it would if we had used a name instead. #### Ignoring Parts of a Value with a Nested `_` We can also use `_` inside of another pattern to ignore just part of a value. -In Listing 18-12, the first `match` arm's pattern matches a `Some` value but +In Listing 18-16, the first `match` arm's pattern matches a `Some` value but ignores the value inside of the `Some` variant as specified by the underscore: ```rust @@ -681,14 +681,14 @@ match x { } ``` -Listing 18-12: Ignoring the value inside of the `Some` +Listing 18-16: Ignoring the value inside of the `Some` variant by using a nested underscore This is useful when the code associated with the `match` arm doesn't use the nested part of the variable at all. We can also use underscores in multiple places within one pattern, as shown in -Listing 18-13 where we're ignoring the second and fourth values in a tuple of +Listing 18-17 where we're ignoring the second and fourth values in a tuple of five items: ```rust @@ -701,7 +701,7 @@ match numbers { } ``` -Listing 18-13: Ignoring multiple parts of a tuple +Listing 18-17: Ignoring multiple parts of a tuple This will print `Some numbers: 2, 8, 32`, and the values 4 and 16 will be ignored. @@ -714,7 +714,7 @@ though, you might create a variable that you'll use eventually, but temporarily it will be unused. If you're in this situation and would like to tell Rust not to warn you about the unused variable, you can start the name of the variable with an underscore. This works just like a variable name in any pattern, only -Rust won't warn you if the variable goes unused. In Listing 18-12, we +Rust won't warn you if the variable goes unused. In Listing 18-18, we do get a warning about not using the variable `y`, but we don't get a warning about not using the variable `_x`: @@ -725,14 +725,14 @@ fn main() { } ``` -Listing 18-12: Starting a variable name with an underscore +Listing 18-18: Starting a variable name with an underscore in order to not get unused variable warnings Note that there is a subtle difference between using only `_` and using a name that starts with an underscore like `_x`: `_x` still binds the value to the variable, but `_` doesn't bind at all. -Listing 18-13 shows a case where this distinction matters: `s` will still be +Listing 18-19 shows a case where this distinction matters: `s` will still be moved into `_s`, which prevents us from using `s` again: ```rust,ignore @@ -743,11 +743,11 @@ let _s = s; println!("{}", s); ``` -Listing 18-13: An unused variable starting with an +Listing 18-19: An unused variable starting with an underscore still binds the value, which may take ownership of the value Using underscore by itself, however, doesn't ever bind to the value. Listing -18-14 will compile without any errors since `s` does not get moved into `_`: +18-20 will compile without any errors since `s` does not get moved into `_`: ```rust let s = String::from("Hello!"); @@ -757,7 +757,7 @@ let _ = s; println!("{}", s); ``` -Listing 18-14: Using underscore does not bind the +Listing 18-20: Using underscore does not bind the value This works just fine. Because we never bind `s` to anything, it's not moved. @@ -767,7 +767,7 @@ This works just fine. Because we never bind `s` to anything, it's not moved. With values that have many parts, we can extract only a few parts and avoid having to list underscores for each remaining part by instead using `..`. The `..` pattern will ignore any parts of a value that we haven't explicitly -matched in the rest of the pattern. In Listing 18-15, we have a `Point` struct +matched in the rest of the pattern. In Listing 18-21, we have a `Point` struct that holds a coordinate in three dimensional space. In the `match` expression, we only want to operate on the `x` coordinate and ignore the values in the `y` and `z` fields: @@ -786,14 +786,14 @@ match origin { } ``` -Listing 18-15: Ignoring all fields of a `Point` except +Listing 18-21: Ignoring all fields of a `Point` except for `x` by using `..` Using `..` is shorter to type than having to list out `_y` and `_z`. The `..` pattern is especially useful when working with structs that have lots of fields in situations where only one or two fields are relevant. -`..` will expand to as many values as it needs to be. Listing 18-16 shows a use +`..` will expand to as many values as it needs to be. Listing 18-22 shows a use of `..` with a tuple: ```rust @@ -808,13 +808,13 @@ fn main() { } ``` -Listing 18-16: Matching only the first and last values in +Listing 18-22: Matching only the first and last values in a tuple and ignoring all other values with `..` Here, we have the first and last value matched, with `first` and `last`. The `..` will match and ignore all of the things in the middle. -Using `..` must be unambiguous, however. Listing 18-17 shows an example where +Using `..` must be unambiguous, however. Listing 18-23 shows an example where it's not clear to Rust which values we want to match and which values we want to ignore: @@ -830,7 +830,7 @@ fn main() { } ``` -Listing 18-17: An attempt to use `..` in a way that is +Listing 18-23: An attempt to use `..` in a way that is ambiguous If we compile this example, we get this error: @@ -856,7 +856,7 @@ since using `..` in two places like this is ambiguous. Usually, when you match against a pattern, the variables that the pattern introduces are bound to a value. This means you'll end up moving the value into the `match` (or wherever you're using the pattern) since the ownership rules -apply. Listing 18-18 shows an example: +apply. Listing 18-24 shows an example: ```rust,ignore let robot_name = Some(String::from("Bors")); @@ -869,7 +869,7 @@ match robot_name { println!("robot_name is: {:?}", robot_name); ``` -Listing 18-18: Creating a variable in a match arm pattern +Listing 18-24: Creating a variable in a match arm pattern takes ownership of the value This example will fail to compile since the value inside the `Some` value in @@ -878,7 +878,7 @@ This example will fail to compile since the value inside the `Some` value in Using `&` in a pattern matches an existing reference in the value, as we saw in the previous section on destructuring. If you want to create a reference instead in order to borrow the value in a pattern variable, use the `ref` -keyword before the new variable, as shown in Listing 18-19: +keyword before the new variable, as shown in Listing 18-25: ```rust let robot_name = Some(String::from("Bors")); @@ -891,7 +891,7 @@ match robot_name { println!("robot_name is: {:?}", robot_name); ``` -Listing 18-19: Creating a reference so that a pattern +Listing 18-25: Creating a reference so that a pattern variable does not take ownership of a value This example will compile because the value in the `Some` variant in @@ -899,7 +899,7 @@ This example will compile because the value in the `Some` variant in only took a reference to the data in `robot_name` rather than moving it. To create a mutable reference, use `ref mut` for the same reason as shown in -Listing 18-20: +Listing 18-26: ```rust let mut robot_name = Some(String::from("Bors")); @@ -912,7 +912,7 @@ match robot_name { println!("robot_name is: {:?}", robot_name); ``` -Listing 18-20: Creating a mutable reference to a value as +Listing 18-26: Creating a mutable reference to a value as part of a pattern using `ref mut` This example will compile and print `robot_name is: Some("Another name")`. @@ -923,7 +923,7 @@ dereference using the `*` operator in order to be able to mutate the value. You can introduce *match guards* as part of a match arm by specifying an additional `if` conditional after the pattern. The conditional can use -variables created in the pattern. Listing 18-21 has a `match` expression with a +variables created in the pattern. Listing 18-27 has a `match` expression with a match guard in the first arm: ```rust @@ -936,7 +936,7 @@ match num { } ``` -Listing 18-21: Adding a match guard to a pattern +Listing 18-27: Adding a match guard to a pattern This example will print `less than five: 4`. If `num` was instead `Some(7)`, this example would print `7`. Match guards allow you to express more complexity @@ -944,7 +944,7 @@ than patterns alone give you. In Listing 18-10, we saw that since patterns shadow variables, we weren't able to specify a pattern to express the case when a value was equal to a variable -outside the `match`. Listing 18-22 shows how we can use a match guard to +outside the `match`. Listing 18-28 shows how we can use a match guard to accomplish this: ```rust @@ -962,7 +962,7 @@ fn main() { } ``` -Listing 18-22: Using a match guard to test for equality +Listing 18-28: Using a match guard to test for equality with an outer variable This will now print `Default case, x = Some(5)`. Because the second match arm @@ -970,9 +970,8 @@ is not introducing a new variable `y` that shadows the outer `y` in the pattern, we can use `y` in the match guard. We're still destructuring `x` to get the inner value `n`, and then we can compare `n` and `y` in the match guard. - If you're using a match guard with multiple patterns specified by `|`, the -match guard condition applies to all of the patterns. Listing 18-23 shows a +match guard condition applies to all of the patterns. Listing 18-29 shows a match guard that applies to the value matched by all three patterns in the first arm: @@ -986,7 +985,7 @@ match x { } ``` -Listing 18-23: Combining multiple patterns with a match +Listing 18-29: Combining multiple patterns with a match guard This prints `no` since the `if` condition applies to the whole pattern `4 | 5 | @@ -1006,7 +1005,7 @@ rather than this: ### `@` Bindings In order to test a value in a pattern but also be able to create a variable -bound to the value, we can use `@`. Listing 18-24 shows an example where we +bound to the value, we can use `@`. Listing 18-30 shows an example where we want to test that a `Message::Hello` `id` field is within the range `3...7` but also be able to bind to the value so that we can use it in the code associated with the arm: @@ -1031,7 +1030,7 @@ match msg { } ``` -Listing 18-24: Using `@` to bind to a value in a pattern +Listing 18-30: Using `@` to bind to a value in a pattern while also testing it This example will print `Found an id in range: 5`. By specifying `id @` before From 3d47ebddad51b0080a19857e1495675a8e9376ef Mon Sep 17 00:00:00 2001 From: "Carol (Nichols || Goulding)" Date: Fri, 7 Apr 2017 19:23:24 -0400 Subject: [PATCH 19/22] Break sections into multiple files --- second-edition/src/SUMMARY.md | 5 +- second-edition/src/ch18-00-patterns.md | 1037 ----------------- .../ch18-01-all-the-places-for-patterns.md | 249 ++++ second-edition/src/ch18-02-refutability.md | 98 ++ second-edition/src/ch18-03-pattern-syntax.md | 687 +++++++++++ 5 files changed, 1038 insertions(+), 1038 deletions(-) create mode 100644 second-edition/src/ch18-01-all-the-places-for-patterns.md create mode 100644 second-edition/src/ch18-02-refutability.md create mode 100644 second-edition/src/ch18-03-pattern-syntax.md diff --git a/second-edition/src/SUMMARY.md b/second-edition/src/SUMMARY.md index 1b915623cc..5d67a1d40f 100644 --- a/second-edition/src/SUMMARY.md +++ b/second-edition/src/SUMMARY.md @@ -99,7 +99,10 @@ ## Advanced Topics -- [Patterns](ch18-00-patterns.md) (perhaps an appendix?) +- [Patterns Match the Structure of Values](ch18-00-patterns.md) + - [All the Places Patterns May be Used](ch18-01-all-the-places-for-patterns.md) + - [Refutability: Whether a Pattern Might Fail to Match](ch18-02-refutability.md) + - [All the Pattern Syntax](ch18-03-pattern-syntax.md) - [Advanced Features](ch19-00-advanced-features.md) - [Unsafe Rust](ch19-01-unsafe-rust.md) diff --git a/second-edition/src/ch18-00-patterns.md b/second-edition/src/ch18-00-patterns.md index 470e96548a..b891c2c283 100644 --- a/second-edition/src/ch18-00-patterns.md +++ b/second-edition/src/ch18-00-patterns.md @@ -17,1040 +17,3 @@ This chapter is a reference on all things related to patterns. We'll cover the valid places to use patterns, the difference between *refutable* and *irrefutable* patterns, and the different kinds of pattern syntax that you might see. - -## All the Places Patterns May be Used - -Patterns pop up in a number of places in Rust. You've been using them a lot -without realizing it! This section is a reference to all the places where -patterns are valid. - -### `match` Arms - -As we discussed in Chapter 6, a common place patterns are used is in the arms -of `match` expressions. Formally, `match` expressions are defined as the -keyword `match`, a value to match on, and one or more match arms that consist -of a pattern and an expression to run if the value matches that arm's pattern: - -```text -match VALUE { - PATTERN => EXPRESSION, - PATTERN => EXPRESSION, - PATTERN => EXPRESSION, -} -``` - -#### Exhaustiveness and the Default Pattern `_` - -`match` expressions are required to be exhaustive. When we put all of the -patterns in the arms together, all possibilities for the value in the `match` -expression must be accounted for. One way to ensure you have every possibility -covered is to have a catch-all pattern for the last arm, like a variable name. -A name matching any value can never fail and thus covers every case remaining -after the previous arms' patterns. - -There's an additional pattern that's often used in the last match arm: `_`. It -matches anything, but it never binds any variables. This can be useful when you -only want to run code for some patterns but ignore any other value, for example. - -### `if let` Expressions - -We discussed `if let` expressions in Chapter 6, and how they're mostly a -shorter way to write the equivalent of a `match` that only cares about matching -one case. `if let` can optionally have a corresponding `else` with code to run -if the pattern in the `if let` doesn't match. - -Listing 18-1 shows that it's even possible to mix and match `if let`, `else -if`, and `else if let`. This code shows a series of checks of a bunch of -different conditions to decide what the background color should be. For the -purposes of the examplee, we've created variables with hardcoded values that a -real program might get by asking the user. If the user has specified a favorite -color, we'll use that as the background color. If today is Tuesday, the -background color will be green. If the user has specified their age as a string -and we can parse it as a number successfully, we'll use either purple or orange -depending on the value of the parsed number. Finally, if none of these -conditions apply, the background color will be blue: - -Filename: src/main.rs - -```rust -fn main() { - let favorite_color: Option<&str> = None; - let is_tuesday = false; - let age: Result = "34".parse(); - - if let Some(color) = favorite_color { - println!("Using your favorite color, {}, as the background", color); - } else if is_tuesday { - println!("Tuesday is green day!"); - } else if let Ok(age) = age { - if age > 30 { - println!("Using purple as the background color"); - } else { - println!("Using orange as the background color"); - } - } else { - println!("Using blue as the background color"); - } -} -``` - -Listing 18-1: Mixing `if let`, `else if`, `else if let`, -and `else` - -This conditional structure lets us support complex requirements. With the -hardcoded values we have here, this example will print `Using purple as the -background color`. - -Note that `if let` can also introduce shadowed variables like `match` arms can: -`if let Ok(age) = age` introduces a new shadowed `age` variable that contains -the value inside the `Ok` variant. This also means the `if age > 30` condition -needs to go within the block; we aren't able to combine these two conditions -into `if let Ok(age) = age && age > 30` since the shadowed `age` that we want -to compare to 30 isn't valid until the new scope starts with the curly brace. - -Also note that conditionals with many cases like these are not as powerful as -`match` expression since exhaustiveness is not checked by the compiler. If we -leave off the last `else` block and miss handling some cases, the compiler will -not error. This example might be too complex to rewrite as a readable `match`, -so we should take extra care to check that we're handling all the cases since -the compiler is not checking exhaustiveness for us. - -### `while let` - -A similar construction to `if let` is `while let`: this allows you to do a -`while` loop as long as a pattern continues to match. Listing 18-2 shows an -example using a `while let` loop to use a vector as a stack and print out the -values in the vector in the opposite order that we pushed the values in: - -```rust -let mut stack = Vec::new(); - -stack.push(1); -stack.push(2); -stack.push(3); - -while let Some(top) = stack.pop() { - println!("{}", top); -} -``` - -Listing 18-2: Using a `while let` loop to print out values -as long as `stack.pop()` returns `Some` - -This example will print 3, 2, then 1. The `pop` method takes the last element -out of the vector and returns `Some(value)`. If the vector is empty, it returns -`None`. The `while` loop will continue running the code in its block as long as -`pop` is returning `Some`. Once it returns `None`, the `while` loop stops. We -can use `while let` to pop every element off our stack. - -### `for` loops - -Looping with `for`, as we discussed in Chapter 3, is the most common loop -construction in Rust code. What we didn't talk about in that chapter was that -`for` takes a pattern. In Listing 18-3, we're demonstrating how we can use a -pattern in a `for` loop to destructure a tuple. The `enumerate` method adapts -an iterator to produce a value and the index of the value in the iterator in a -tuple: - -```rust -let v = vec![1, 2, 3]; - -for (index, value) in v.iter().enumerate() { - println!("{} is at index {}", value, index); -} -``` - -Listing 18-3: Using a pattern in a `for` loop to -destructure the tuple returned from `enumerate` into its pieces - -This will print: - -```text -1 is at index 0 -2 is at index 1 -3 is at index 2 -``` - -The first call to `enumerate` produces the tuple `(0, 1)`. When this value is -matched to the pattern `(index, value)`, `index` will be 0 and `value` will -equal 1. - -### `let` Statements - -`match` and `if let` are the places we've explicitly discussed using patterns -earlier in the book, but they aren't the only places we've *used* patterns. For -example, consider this straightforward variable assignment with `let`: - -```rust -let x = 5; -``` - -We've done this hundreds of times throughout this book. You may not have -realized it, but you were using patterns! A `let` statement looks like this, -more formally: - -```text -let PATTERN = EXPRESSION; -``` - -We've seen statements like `let x = 5;` with a variable name in the `PATTERN` -slot; a variable name is just a particularly humble form of pattern. - -With `let`, we compare the expression against the pattern, and assign any names -we find. So for example, in our `let x = 5;` case, `x` is a pattern that says -"bind what matches here to the variable `x`. And since the name `x` is the -whole pattern, this pattern effectively means "bind everything to the variable -`x`, whatever the value is." - -To see the pattern matching aspect of `let` a bit more clearly, consider -Listing 18-4 where we're using a pattern with `let` to destructuring a tuple: - -```rust -let (x, y, z) = (1, 2, 3); -``` - -Listing 18-4: Using a pattern to destructure a tuple and -create 3 variables at once - -Here, we have a tuple that we're matching against a pattern. Rust will compare -the value `(1, 2, 3)` to the pattern `(x, y, z)` and see that the value matches -the pattern. In this case, it will bind `1` to `x`, `2` to `y`, and `3` to `z`. -You can think of this tuple pattern as nesting three individual variable -patterns inside of it. - -We saw another example of destructuring a tuple in Chapter 16, Listing 16-6, -where we destructured the return value of `mpsc::channel()` into the `tx` -(transmitter) and `rx` (receiver) parts. - -### Function Parameters - -Similarly to `let`, function parameters can also be patterns. The code in -Listing 18-5 declaring a function named `foo` that takes one parameter named -`x` of type `i32` should look familiar: - -```rust -fn foo(x: i32) { - // code goes here -} -``` - -Listing 18-5: A function signature uses patterns in the -parameters - -The `x` part is a pattern! In a similar way as we did with `let`, we could -match a tuple in a function's arguments. Listing 18-6 shows how we could split -apart the values in a tuple as part of passing the tuple to a function: - -Filename: src/main.rs - -```rust -fn print_coordinates(&(x, y): &(i32, i32)) { - println!("Current location: ({}, {})", x, y); -} - -fn main() { - let point = (3, 5); - print_coordinates(&point); -} -``` - -Listing 18-6: A function with parameters that destructure -a tuple - -This will print `Current location: (3, 5)`. When we pass the value `&(3, 5)` to -`print_coordinates`, the values match the pattern `&(x, y)`. `x` gets the value -3, and `y` gets the value 5. - -Because closures are similar to functions, as we discussed in Chapter 13, we -can use patterns in closure parameter lists as well. - -One difference between the places we can use patterns is that with `for` loops, -`let`, and in function parameters, the patterns must be *irrefutable*. Let's -discuss that next. - -## Refutability: Whether a Pattern Might Fail to Match - -Patterns come in two forms: refutable and irrefutable. Patterns which cannot -fail to match for any possible value are *irrefutable*, and patterns which can -fail to match for some possible value are said to be *refutable*. `let` -statements, function parameters, and `for` loops are restricted to only accept -irrefutable patterns, since there's nothing correct the program could do if the -pattern fails to match. `match`, `if let`, and `while let` expressions are -restricted to only accept refutable patterns, since they're made to handle -possible failure and we wouldn't need their functionality if the pattern could -never fail. - -In general, you shouldn't have to worry about the distinction between refutable -and irrefutable patterns; just be familiar with the concept of refutability -when you see it mentioned in an error message. When you get an error message -involving refutability, you'll need to change either the pattern or the -construct you're using the pattern with, depending on your intentions for the -behavior of the code. - -Let's look at some examples. Earlier in this chapter, we had `let x = 5;`. `x` -is indeed an irrefutable pattern we're allowed to use: since it matches -anything, it can't fail to match. In contrast, consider trying to match one -variant of an enum with `let`, such as matching only a `Some` value from the -`Option` enum as shown in Listing 18-7: - -```rust,ignore -let Some(x) = some_option_value; -``` - -Listing 18-7: Attempting to use a refutable pattern with -`let` - -If `some_option_value` was a `None` value, `some_option_value` would not match -the pattern `Some(x)`. The pattern `Some(x)` is refutable since there exists a -case in which it would fail to match a value. There's nothing valid that our -code could do with this `let` statement if `some_option_value` was the `None` -value. Therefore, Rust will complain at compile time that we've tried to use a -refutable pattern where an irrefutable pattern is required: - -```text -error[E0005]: refutable pattern in local binding: `None` not covered - --> :3:5 - | -3 | let Some(x) = some_option_value; - | ^^^^^^^ pattern `None` not covered -``` - -We didn't cover (and couldn't cover!) every valid value with the pattern -`Some(x)`, so Rust will rightfully complain. - -If we have a refutable pattern, instead of using `let`, we can use `if let`. -That way, if the pattern doesn't match, the code inside the curly braces won't -execute. That code will only make sense and run if the value matches the -pattern. Listing 18-8 shows how to fix the code in Listing 18-7 with `Some(x)` -matching `some_option_value`. Using the refutable pattern `Some(x)` is allowed, -since this example uses `if let`: - -```rust -# let some_option_value: Option = None; -if let Some(x) = some_option_value { - println!("{}", x); -} -``` - -Listing 18-8: Using `if let` and a block with refutable -patterns instead of `let` - -Consequently, if we give `if let` an irrefutable pattern that will always match, -such as `x` as shown in Listing 18-9: - -```rust,ignore -if let x = 5 { - println!("{}", x); -}; -``` - -Listing 18-9: Attempting to use an irrefutable pattern -with `if let` - -Rust will complain that it doesn't make sense to use `if let` with an -irrefutable pattern: - -```text -error[E0162]: irrefutable if-let pattern - --> :2:8 - | -2 | if let x = 5 { - | ^ irrefutable pattern -``` - -Generally, match arms use refutable patterns, except for the last arm that -might match any remaining values with an irrefutable pattern. A `match` with -only one arm whose pattern is irrefutable is allowed, but it's not particularly -useful and could be replaced with a simpler `let` statement. - -Now that we've discussed all the places that patterns can be used and the -difference between refutable and irrefutable patterns, let's go over all the -syntax we can use to create patterns. - -## All the Pattern Syntax - -We've seen some examples of different kinds of patterns throughout the book. -This section lists all the syntax valid in patterns and why you might want to -use each of them. - -### Literals - -As we saw in Chapter 6, you can match against literals directly: - -```rust -let x = 1; - -match x { - 1 => println!("one"), - 2 => println!("two"), - 3 => println!("three"), - _ => println!("anything"), -} -``` - -This prints `one` since the value in `x` is 1. - -### Named Variables - -Named variables are irrefutable patterns that match any value. - -As with all variables, variables declared as part of a pattern will shadow -variables with the same name outside of the `match` construct since a `match` -starts a new scope. In Listing 18-10, we declare a variable named `x` with the -value `Some(5)` and a variable `y` with the value `10`. Then we have a `match` -expression on the value `x`. Take a look at the patterns in the match arms and -the `println!` at the end, and make a guess about what will be printed before -running this code or reading further: - -Filename: src/main.rs - -```rust -fn main() { - 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 = {:?}, y = {:?}", x, y); -} -``` - -Listing 18-10: A `match` statement with an arm that -introduces a shadowed variable `y` - -Let's walk through what happens when the `match` statement runs. The first -match arm has the pattern `Some(50)`, and the value in `x` (`Some(5)`) does not -match `Some(50)`, so we continue. In the second match arm, the pattern -`Some(y)` introduces a new variable name `y` that will match any value inside a -`Some` value. Because we're in a new scope inside the `match` expression, this -is a new variable, not the `y` we declared at the beginning that has the value -10. The new `y` binding will match any value inside a `Some`, which is what we -have in `x`, so we execute the expression for that arm and print `Matched, y = -5` since this `y` binds to the inner value of the `Some` in `x`, which is 5. - -If `x` had been a `None` value instead of `Some(5)`, we would have matched the -underscore since the other two arms' patterns would not have matched. In the -expression for that match arm, since we did not introduce an `x` variable in -the pattern of the arm, this `x` is still the outer `x` that has not been -shadowed. In this hypothetical case, the `match` would print `Default case, x = -None`. - -Once the `match` expression is over, its scope ends, and so does the scope of -the inner `y`. The last `println!` produces `at the end: x = Some(5), y = 10`. - -In order to make a `match` expression that compares the values of the outer `x` -and `y` rather than introducing a shadowed variable, we would need to use a -match guard conditional instead. We'll be talking about match guards later in -this section. - -### Multiple patterns - -You can match multiple patterns with `|`, which means *or*: - -```rust -let x = 1; - -match x { - 1 | 2 => println!("one or two"), - 3 => println!("three"), - _ => println!("anything"), -} -``` - -This prints `one or two`. - -### Matching Ranges of Values with `...` - -You can match a range of values with `...`: - -```rust -let x = 5; - -match x { - 1 ... 5 => println!("one through five"), - _ => println!("something else"), -} -``` - -Ranges are only allowed with numeric values or `char` values. Here's an example -using ranges of `char` values: - -```rust -let x = 'c'; - -match x { - 'a' ... 'j' => println!("early ASCII letter"), - 'k' ... 'z' => println!("late ASCII letter"), - _ => println!("something else"), -} -``` - -This will print `early ASCII letter`. - -### Destructuring to Break Apart Values - -Patterns can be used to *destructure* structs, enums, tuples, and references. -Destructuring means to break a value up into its component pieces. Listing -18-11 shows a `Point` struct with two fields, `x` and `y`, that we can break -apart by using a pattern with a `let` statement: - -Filename: src/main.rs - -```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); -} -``` - -Listing 18-11: Destructuring using struct field -shorthand - -This creates the variables `x` and `y` that match the `x` and `y` of `p`. The -names of the variables must match the names of the fields to use this -shorthand. If we wanted to use names different than the variable names, we can -specify `field_name: variable_name` in the pattern. In Listing 18-12, `a` will -have the value in the `Point` instance's `x` field and `b` will have the value -in the `y` field: - -Filename: src/main.rs - -```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); -} -``` - -Listing 18-12: Destructuring struct fields into variables -with different names than the fields - -We can also use destructuring with literal values in order to test and use -inner parts of a value. Listing 18-13 shows a `match` statement that determines -whether a point lies directly on the `x` axis (which is true when `y = 0`), on -the `y` axis (`x = 0`), or neither: - -```rust -# struct Point { -# x: i32, -# y: i32, -# } -# -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), - } -} -``` - -Listing 18-13: Destructuring and matching literal values -in one pattern - -This will print `On the y axis at 7` since the value `p` matches the second arm -by virtue of `x` having the value 0. - -We used desctructuring on enums in Chapter 6, such as in Listing 6-5 where we -destructured an `Option` using a `match` expression and added one to the -inner value of the `Some` variant. - -When the value we're matching against a pattern contains a reference, we can -specify a `&` in the pattern in order to separate the reference and the value. -This is especially useful in closures used with iterators that iterate over -references to values when we want to use the values in the closure rather than -the references. Listing 18-14 shows how to iterate over references to `Point` -instances in a vector, and destructure both the reference and the struct in -order to be able to perform calculations on the `x` and `y` values easily: - -```rust -# struct Point { -# x: i32, -# y: i32, -# } -# -let points = vec![ - Point { x: 0, y: 0}, - Point { x: 1, y: 5}, - Point { x: 10, y: -3}, -]; -let sum_of_squares: i32 = points - .iter() - .map(|&Point {x, y}| x * x + y * y) - .sum(); -``` - -Listing 18-14: Destructuring a reference to a struct into -the struct field values - -Because `iter` iterates over references to the items in the vector, if we -forgot the `&` in the closure arguments in the `map`, we'd get a type mismatch -error like this: - -```text -error[E0308]: mismatched types - --> - | -14 | .map(|Point {x, y}| x * x + y * y) - | ^^^^^^^^^^^^ expected &Point, found struct `Point` - | - = note: expected type `&Point` - found type `Point` -``` - -This says Rust was expecting our closure to match `&Point`, but we tried to -match the value with a pattern that was a `Point` value, not a reference to a -`Point`. - -We can mix, match, and nest destructuring patterns in even more complex ways: -we can do something complicated like this example where we nest structs and and -tuples inside of a tuple and destructure all the primitive values out: - -```rust -# struct Point { -# x: i32, -# y: i32, -# } -# -let ((feet, inches), Point {x, y}) = ((3, 10), Point { x: 3, y: -10 }); -``` - -This lets us break complex types into their component parts. - -### Ignoring Values in a Pattern - -There are a few ways to ignore entire values or parts of values: using the `_` -pattern, using the `_` pattern within another pattern, using a name that starts -with an underscore, or using `..` to ignore all remaining parts of a value. -Let's explore how and why to do each of these. - -#### Ignoring an Entire Value with `_` - -We've seen the use of underscore as a wildcard pattern that will match any value -but not bind to the value. While the underscore pattern is especially useful as -the last arm in a `match` expression, we can use it in any pattern, such as -function arguments as shown in Listing 18-15: - -```rust -fn foo(_: i32) { - // code goes here -} -``` - -Listing 18-15: Using `_` in a function signature - -Normally, you would change the signature to not have the unused parameter. In -cases such as implementing a trait, where you need a certain type signature, -using an underscore lets you ignore a parameter, and the compiler won't warn -about unused function parameters like it would if we had used a name instead. - -#### Ignoring Parts of a Value with a Nested `_` - -We can also use `_` inside of another pattern to ignore just part of a value. -In Listing 18-16, the first `match` arm's pattern matches a `Some` value but -ignores the value inside of the `Some` variant as specified by the underscore: - -```rust -let x = Some(5); - -match x { - Some(_) => println!("got a Some and I don't care what's inside"), - None => (), -} -``` - -Listing 18-16: Ignoring the value inside of the `Some` -variant by using a nested underscore - -This is useful when the code associated with the `match` arm doesn't use the -nested part of the variable at all. - -We can also use underscores in multiple places within one pattern, as shown in -Listing 18-17 where we're ignoring the second and fourth values in a tuple of -five items: - -```rust -let numbers = (2, 4, 8, 16, 32); - -match numbers { - (first, _, third, _, fifth) => { - println!("Some numbers: {}, {}, {}", first, third, fifth) - }, -} -``` - -Listing 18-17: Ignoring multiple parts of a tuple - -This will print `Some numbers: 2, 8, 32`, and the values 4 and 16 will be -ignored. - -#### Ignoring an Unused Variable by Starting its Name with an Underscore - -Usually, Rust will warn you if you create a variable but don't use it anywhere, -since that could be a bug. If you're prototyping or just starting a project, -though, you might create a variable that you'll use eventually, but temporarily -it will be unused. If you're in this situation and would like to tell Rust not -to warn you about the unused variable, you can start the name of the variable -with an underscore. This works just like a variable name in any pattern, only -Rust won't warn you if the variable goes unused. In Listing 18-18, we -do get a warning about not using the variable `y`, but we don't get a warning -about not using the variable `_x`: - -```rust -fn main() { - let _x = 5; - let y = 10; -} -``` - -Listing 18-18: Starting a variable name with an underscore -in order to not get unused variable warnings - -Note that there is a subtle difference between using only `_` and using a name -that starts with an underscore like `_x`: `_x` still binds the value to the -variable, but `_` doesn't bind at all. - -Listing 18-19 shows a case where this distinction matters: `s` will still be -moved into `_s`, which prevents us from using `s` again: - -```rust,ignore -let s = String::from("Hello!"); - -let _s = s; - -println!("{}", s); -``` - -Listing 18-19: An unused variable starting with an -underscore still binds the value, which may take ownership of the value - -Using underscore by itself, however, doesn't ever bind to the value. Listing -18-20 will compile without any errors since `s` does not get moved into `_`: - -```rust -let s = String::from("Hello!"); - -let _ = s; - -println!("{}", s); -``` - -Listing 18-20: Using underscore does not bind the -value - -This works just fine. Because we never bind `s` to anything, it's not moved. - -### Ignoring Remaining Parts of a Value with `..` - -With values that have many parts, we can extract only a few parts and avoid -having to list underscores for each remaining part by instead using `..`. The -`..` pattern will ignore any parts of a value that we haven't explicitly -matched in the rest of the pattern. In Listing 18-21, we have a `Point` struct -that holds a coordinate in three dimensional space. In the `match` expression, -we only want to operate on the `x` coordinate and ignore the values in the `y` -and `z` fields: - -```rust -struct Point { - x: i32, - y: i32, - z: i32, -} - -let origin = Point { x: 0, y: 0, z: 0 }; - -match origin { - Point { x, .. } => println!("x is {}", x), -} -``` - -Listing 18-21: Ignoring all fields of a `Point` except -for `x` by using `..` - -Using `..` is shorter to type than having to list out `_y` and `_z`. The `..` -pattern is especially useful when working with structs that have lots of fields -in situations where only one or two fields are relevant. - -`..` will expand to as many values as it needs to be. Listing 18-22 shows a use -of `..` with a tuple: - -```rust -fn main() { - let numbers = (2, 4, 8, 16, 32); - - match numbers { - (first, .., last) => { - println!("Some numbers: {}, {}", first, last); - }, - } -} -``` - -Listing 18-22: Matching only the first and last values in -a tuple and ignoring all other values with `..` - -Here, we have the first and last value matched, with `first` and `last`. The -`..` will match and ignore all of the things in the middle. - -Using `..` must be unambiguous, however. Listing 18-23 shows an example where -it's not clear to Rust which values we want to match and which values we want -to ignore: - -```rust,ignore -fn main() { - let numbers = (2, 4, 8, 16, 32); - - match numbers { - (.., second, ..) => { - println!("Some numbers: {}", second) - }, - } -} -``` - -Listing 18-23: An attempt to use `..` in a way that is -ambiguous - -If we compile this example, we get this error: - -```text -error: `..` can only be used once per tuple or tuple struct pattern - --> src/main.rs:5:22 - | -5 | (.., second, ..) => { - | ^^ -``` - -It's not possible to determine how many values in the tuple should be ignored -before one value is matched with `second`, and then how many further values are -ignored after that. We could mean that we want to ignore 2, bind `second` to 4, -then ignore 8, 16, and 32, or we could mean that we want to ignore 2 and 4, -bind `second` to 8, then ignore 16 and 32, and so forth. The variable name -`second` doesn't mean anything special to Rust, so we get a compiler error -since using `..` in two places like this is ambiguous. - -### `ref` and `ref mut` to Create References in Patterns - -Usually, when you match against a pattern, the variables that the pattern -introduces are bound to a value. This means you'll end up moving the value into -the `match` (or wherever you're using the pattern) since the ownership rules -apply. Listing 18-24 shows an example: - -```rust,ignore -let robot_name = Some(String::from("Bors")); - -match robot_name { - Some(name) => println!("Found a name: {}", name), - None => (), -} - -println!("robot_name is: {:?}", robot_name); -``` - -Listing 18-24: Creating a variable in a match arm pattern -takes ownership of the value - -This example will fail to compile since the value inside the `Some` value in -`robot_name` is moved within the `match` when `name` binds to that value. - -Using `&` in a pattern matches an existing reference in the value, as we saw in -the previous section on destructuring. If you want to create a reference -instead in order to borrow the value in a pattern variable, use the `ref` -keyword before the new variable, as shown in Listing 18-25: - -```rust -let robot_name = Some(String::from("Bors")); - -match robot_name { - Some(ref name) => println!("Found a name: {}", name), - None => (), -} - -println!("robot_name is: {:?}", robot_name); -``` - -Listing 18-25: Creating a reference so that a pattern -variable does not take ownership of a value - -This example will compile because the value in the `Some` variant in -`robot_name` is not moved into the `Some(ref name)` arm of the match; the match -only took a reference to the data in `robot_name` rather than moving it. - -To create a mutable reference, use `ref mut` for the same reason as shown in -Listing 18-26: - -```rust -let mut robot_name = Some(String::from("Bors")); - -match robot_name { - Some(ref mut name) => *name = String::from("Another name"), - None => (), -} - -println!("robot_name is: {:?}", robot_name); -``` - -Listing 18-26: Creating a mutable reference to a value as -part of a pattern using `ref mut` - -This example will compile and print `robot_name is: Some("Another name")`. -Since `name` is a mutable reference, within the match arm code, we need to -dereference using the `*` operator in order to be able to mutate the value. - -### Extra Conditionals with Match Guards - -You can introduce *match guards* as part of a match arm by specifying an -additional `if` conditional after the pattern. The conditional can use -variables created in the pattern. Listing 18-27 has a `match` expression with a -match guard in the first arm: - -```rust -let num = Some(4); - -match num { - Some(x) if x < 5 => println!("less than five: {}", x), - Some(x) => println!("{}", x), - None => (), -} -``` - -Listing 18-27: Adding a match guard to a pattern - -This example will print `less than five: 4`. If `num` was instead `Some(7)`, -this example would print `7`. Match guards allow you to express more complexity -than patterns alone give you. - -In Listing 18-10, we saw that since patterns shadow variables, we weren't able -to specify a pattern to express the case when a value was equal to a variable -outside the `match`. Listing 18-28 shows how we can use a match guard to -accomplish this: - -```rust -fn main() { - let x = Some(5); - let y = 10; - - match x { - Some(50) => println!("Got 50"), - Some(n) if n == y => println!("Matched, n = {:?}", n), - _ => println!("Default case, x = {:?}", x), - } - - println!("at the end: x = {:?}, y = {:?}", x, y); -} -``` - -Listing 18-28: Using a match guard to test for equality -with an outer variable - -This will now print `Default case, x = Some(5)`. Because the second match arm -is not introducing a new variable `y` that shadows the outer `y` in the -pattern, we can use `y` in the match guard. We're still destructuring `x` to -get the inner value `n`, and then we can compare `n` and `y` in the match guard. - -If you're using a match guard with multiple patterns specified by `|`, the -match guard condition applies to all of the patterns. Listing 18-29 shows a -match guard that applies to the value matched by all three patterns in the -first arm: - -```rust -let x = 4; -let y = false; - -match x { - 4 | 5 | 6 if y => println!("yes"), - _ => println!("no"), -} -``` - -Listing 18-29: Combining multiple patterns with a match -guard - -This prints `no` since the `if` condition applies to the whole pattern `4 | 5 | -6`, not only to the last value `6`. In other words, the precedence of a match -guard in relation to a pattern behaves like this: - -```text -(4 | 5 | 6) if y => ... -``` - -rather than this: - -```text -4 | 5 | (6 if y) => ... -``` - -### `@` Bindings - -In order to test a value in a pattern but also be able to create a variable -bound to the value, we can use `@`. Listing 18-30 shows an example where we -want to test that a `Message::Hello` `id` field is within the range `3...7` but -also be able to bind to the value so that we can use it in the code associated -with the arm: - -```rust -enum Message { - Hello { id: i32 }, -} - -let msg = Message::Hello { id: 5 }; - -match msg { - Message::Hello { id: id @ 3...7 } => { - println!("Found an id in range: {}", id) - }, - Message::Hello { id: 10...12 } => { - println!("Found an id in another range") - }, - Message::Hello { id } => { - println!("Found some other id: {}", id) - }, -} -``` - -Listing 18-30: Using `@` to bind to a value in a pattern -while also testing it - -This example will print `Found an id in range: 5`. By specifying `id @` before -the range, we're capturing whatever value matched the range while also testing -it. In the second arm where we only have a range specified in the pattern, the -code associated with the arm doesn't know if `id` is 10, 11, or 12, since we -haven't saved the `id` value in a variable: we only know that the value matched -something in that range if that arm's code is executed. In the last arm where -we've specified a variable without a range, we do have the value available to -use in the arm's code, but we haven't applied any other test to the value. -Using `@` lets us test a value and save it in a variable within one pattern. - -## Summary - -Patterns are a useful feature of Rust that help to distinguish between -different kinds of data. When used in `match` statements, Rust makes sure that -your patterns cover every possible value. Patterns in `let` statements and -function parameters make those constructs more powerful, enabling the -destructuring of values into smaller parts at the same time as assigning to -variables. - -Now, for the penultimate chapter of the book, let's take a look at some -advanced parts of a variety of Rust's features. diff --git a/second-edition/src/ch18-01-all-the-places-for-patterns.md b/second-edition/src/ch18-01-all-the-places-for-patterns.md new file mode 100644 index 0000000000..de5aac57ff --- /dev/null +++ b/second-edition/src/ch18-01-all-the-places-for-patterns.md @@ -0,0 +1,249 @@ +## All the Places Patterns May be Used + +Patterns pop up in a number of places in Rust. You've been using them a lot +without realizing it! This section is a reference to all the places where +patterns are valid. + +### `match` Arms + +As we discussed in Chapter 6, a common place patterns are used is in the arms +of `match` expressions. Formally, `match` expressions are defined as the +keyword `match`, a value to match on, and one or more match arms that consist +of a pattern and an expression to run if the value matches that arm's pattern: + +```text +match VALUE { + PATTERN => EXPRESSION, + PATTERN => EXPRESSION, + PATTERN => EXPRESSION, +} +``` + +#### Exhaustiveness and the Default Pattern `_` + +`match` expressions are required to be exhaustive. When we put all of the +patterns in the arms together, all possibilities for the value in the `match` +expression must be accounted for. One way to ensure you have every possibility +covered is to have a catch-all pattern for the last arm, like a variable name. +A name matching any value can never fail and thus covers every case remaining +after the previous arms' patterns. + +There's an additional pattern that's often used in the last match arm: `_`. It +matches anything, but it never binds any variables. This can be useful when you +only want to run code for some patterns but ignore any other value, for example. + +### `if let` Expressions + +We discussed `if let` expressions in Chapter 6, and how they're mostly a +shorter way to write the equivalent of a `match` that only cares about matching +one case. `if let` can optionally have a corresponding `else` with code to run +if the pattern in the `if let` doesn't match. + +Listing 18-1 shows that it's even possible to mix and match `if let`, `else +if`, and `else if let`. This code shows a series of checks of a bunch of +different conditions to decide what the background color should be. For the +purposes of the examplee, we've created variables with hardcoded values that a +real program might get by asking the user. If the user has specified a favorite +color, we'll use that as the background color. If today is Tuesday, the +background color will be green. If the user has specified their age as a string +and we can parse it as a number successfully, we'll use either purple or orange +depending on the value of the parsed number. Finally, if none of these +conditions apply, the background color will be blue: + +Filename: src/main.rs + +```rust +fn main() { + let favorite_color: Option<&str> = None; + let is_tuesday = false; + let age: Result = "34".parse(); + + if let Some(color) = favorite_color { + println!("Using your favorite color, {}, as the background", color); + } else if is_tuesday { + println!("Tuesday is green day!"); + } else if let Ok(age) = age { + if age > 30 { + println!("Using purple as the background color"); + } else { + println!("Using orange as the background color"); + } + } else { + println!("Using blue as the background color"); + } +} +``` + +Listing 18-1: Mixing `if let`, `else if`, `else if let`, +and `else` + +This conditional structure lets us support complex requirements. With the +hardcoded values we have here, this example will print `Using purple as the +background color`. + +Note that `if let` can also introduce shadowed variables like `match` arms can: +`if let Ok(age) = age` introduces a new shadowed `age` variable that contains +the value inside the `Ok` variant. This also means the `if age > 30` condition +needs to go within the block; we aren't able to combine these two conditions +into `if let Ok(age) = age && age > 30` since the shadowed `age` that we want +to compare to 30 isn't valid until the new scope starts with the curly brace. + +Also note that conditionals with many cases like these are not as powerful as +`match` expression since exhaustiveness is not checked by the compiler. If we +leave off the last `else` block and miss handling some cases, the compiler will +not error. This example might be too complex to rewrite as a readable `match`, +so we should take extra care to check that we're handling all the cases since +the compiler is not checking exhaustiveness for us. + +### `while let` + +A similar construction to `if let` is `while let`: this allows you to do a +`while` loop as long as a pattern continues to match. Listing 18-2 shows an +example using a `while let` loop to use a vector as a stack and print out the +values in the vector in the opposite order that we pushed the values in: + +```rust +let mut stack = Vec::new(); + +stack.push(1); +stack.push(2); +stack.push(3); + +while let Some(top) = stack.pop() { + println!("{}", top); +} +``` + +Listing 18-2: Using a `while let` loop to print out values +as long as `stack.pop()` returns `Some` + +This example will print 3, 2, then 1. The `pop` method takes the last element +out of the vector and returns `Some(value)`. If the vector is empty, it returns +`None`. The `while` loop will continue running the code in its block as long as +`pop` is returning `Some`. Once it returns `None`, the `while` loop stops. We +can use `while let` to pop every element off our stack. + +### `for` loops + +Looping with `for`, as we discussed in Chapter 3, is the most common loop +construction in Rust code. What we didn't talk about in that chapter was that +`for` takes a pattern. In Listing 18-3, we're demonstrating how we can use a +pattern in a `for` loop to destructure a tuple. The `enumerate` method adapts +an iterator to produce a value and the index of the value in the iterator in a +tuple: + +```rust +let v = vec![1, 2, 3]; + +for (index, value) in v.iter().enumerate() { + println!("{} is at index {}", value, index); +} +``` + +Listing 18-3: Using a pattern in a `for` loop to +destructure the tuple returned from `enumerate` into its pieces + +This will print: + +```text +1 is at index 0 +2 is at index 1 +3 is at index 2 +``` + +The first call to `enumerate` produces the tuple `(0, 1)`. When this value is +matched to the pattern `(index, value)`, `index` will be 0 and `value` will +equal 1. + +### `let` Statements + +`match` and `if let` are the places we've explicitly discussed using patterns +earlier in the book, but they aren't the only places we've *used* patterns. For +example, consider this straightforward variable assignment with `let`: + +```rust +let x = 5; +``` + +We've done this hundreds of times throughout this book. You may not have +realized it, but you were using patterns! A `let` statement looks like this, +more formally: + +```text +let PATTERN = EXPRESSION; +``` + +We've seen statements like `let x = 5;` with a variable name in the `PATTERN` +slot; a variable name is just a particularly humble form of pattern. + +With `let`, we compare the expression against the pattern, and assign any names +we find. So for example, in our `let x = 5;` case, `x` is a pattern that says +"bind what matches here to the variable `x`. And since the name `x` is the +whole pattern, this pattern effectively means "bind everything to the variable +`x`, whatever the value is." + +To see the pattern matching aspect of `let` a bit more clearly, consider +Listing 18-4 where we're using a pattern with `let` to destructuring a tuple: + +```rust +let (x, y, z) = (1, 2, 3); +``` + +Listing 18-4: Using a pattern to destructure a tuple and +create 3 variables at once + +Here, we have a tuple that we're matching against a pattern. Rust will compare +the value `(1, 2, 3)` to the pattern `(x, y, z)` and see that the value matches +the pattern. In this case, it will bind `1` to `x`, `2` to `y`, and `3` to `z`. +You can think of this tuple pattern as nesting three individual variable +patterns inside of it. + +We saw another example of destructuring a tuple in Chapter 16, Listing 16-6, +where we destructured the return value of `mpsc::channel()` into the `tx` +(transmitter) and `rx` (receiver) parts. + +### Function Parameters + +Similarly to `let`, function parameters can also be patterns. The code in +Listing 18-5 declaring a function named `foo` that takes one parameter named +`x` of type `i32` should look familiar: + +```rust +fn foo(x: i32) { + // code goes here +} +``` + +Listing 18-5: A function signature uses patterns in the +parameters + +The `x` part is a pattern! In a similar way as we did with `let`, we could +match a tuple in a function's arguments. Listing 18-6 shows how we could split +apart the values in a tuple as part of passing the tuple to a function: + +Filename: src/main.rs + +```rust +fn print_coordinates(&(x, y): &(i32, i32)) { + println!("Current location: ({}, {})", x, y); +} + +fn main() { + let point = (3, 5); + print_coordinates(&point); +} +``` + +Listing 18-6: A function with parameters that destructure +a tuple + +This will print `Current location: (3, 5)`. When we pass the value `&(3, 5)` to +`print_coordinates`, the values match the pattern `&(x, y)`. `x` gets the value +3, and `y` gets the value 5. + +Because closures are similar to functions, as we discussed in Chapter 13, we +can use patterns in closure parameter lists as well. + +One difference between the places we can use patterns is that with `for` loops, +`let`, and in function parameters, the patterns must be *irrefutable*. Let's +discuss that next. diff --git a/second-edition/src/ch18-02-refutability.md b/second-edition/src/ch18-02-refutability.md new file mode 100644 index 0000000000..17669c5e08 --- /dev/null +++ b/second-edition/src/ch18-02-refutability.md @@ -0,0 +1,98 @@ +## Refutability: Whether a Pattern Might Fail to Match + +Patterns come in two forms: refutable and irrefutable. Patterns which cannot +fail to match for any possible value are *irrefutable*, and patterns which can +fail to match for some possible value are said to be *refutable*. `let` +statements, function parameters, and `for` loops are restricted to only accept +irrefutable patterns, since there's nothing correct the program could do if the +pattern fails to match. `match`, `if let`, and `while let` expressions are +restricted to only accept refutable patterns, since they're made to handle +possible failure and we wouldn't need their functionality if the pattern could +never fail. + +In general, you shouldn't have to worry about the distinction between refutable +and irrefutable patterns; just be familiar with the concept of refutability +when you see it mentioned in an error message. When you get an error message +involving refutability, you'll need to change either the pattern or the +construct you're using the pattern with, depending on your intentions for the +behavior of the code. + +Let's look at some examples. Earlier in this chapter, we had `let x = 5;`. `x` +is indeed an irrefutable pattern we're allowed to use: since it matches +anything, it can't fail to match. In contrast, consider trying to match one +variant of an enum with `let`, such as matching only a `Some` value from the +`Option` enum as shown in Listing 18-7: + +```rust,ignore +let Some(x) = some_option_value; +``` + +Listing 18-7: Attempting to use a refutable pattern with +`let` + +If `some_option_value` was a `None` value, `some_option_value` would not match +the pattern `Some(x)`. The pattern `Some(x)` is refutable since there exists a +case in which it would fail to match a value. There's nothing valid that our +code could do with this `let` statement if `some_option_value` was the `None` +value. Therefore, Rust will complain at compile time that we've tried to use a +refutable pattern where an irrefutable pattern is required: + +```text +error[E0005]: refutable pattern in local binding: `None` not covered + --> :3:5 + | +3 | let Some(x) = some_option_value; + | ^^^^^^^ pattern `None` not covered +``` + +We didn't cover (and couldn't cover!) every valid value with the pattern +`Some(x)`, so Rust will rightfully complain. + +If we have a refutable pattern, instead of using `let`, we can use `if let`. +That way, if the pattern doesn't match, the code inside the curly braces won't +execute. That code will only make sense and run if the value matches the +pattern. Listing 18-8 shows how to fix the code in Listing 18-7 with `Some(x)` +matching `some_option_value`. Using the refutable pattern `Some(x)` is allowed, +since this example uses `if let`: + +```rust +# let some_option_value: Option = None; +if let Some(x) = some_option_value { + println!("{}", x); +} +``` + +Listing 18-8: Using `if let` and a block with refutable +patterns instead of `let` + +Consequently, if we give `if let` an irrefutable pattern that will always match, +such as `x` as shown in Listing 18-9: + +```rust,ignore +if let x = 5 { + println!("{}", x); +}; +``` + +Listing 18-9: Attempting to use an irrefutable pattern +with `if let` + +Rust will complain that it doesn't make sense to use `if let` with an +irrefutable pattern: + +```text +error[E0162]: irrefutable if-let pattern + --> :2:8 + | +2 | if let x = 5 { + | ^ irrefutable pattern +``` + +Generally, match arms use refutable patterns, except for the last arm that +might match any remaining values with an irrefutable pattern. A `match` with +only one arm whose pattern is irrefutable is allowed, but it's not particularly +useful and could be replaced with a simpler `let` statement. + +Now that we've discussed all the places that patterns can be used and the +difference between refutable and irrefutable patterns, let's go over all the +syntax we can use to create patterns. diff --git a/second-edition/src/ch18-03-pattern-syntax.md b/second-edition/src/ch18-03-pattern-syntax.md new file mode 100644 index 0000000000..cf7b4e958c --- /dev/null +++ b/second-edition/src/ch18-03-pattern-syntax.md @@ -0,0 +1,687 @@ +## All the Pattern Syntax + +We've seen some examples of different kinds of patterns throughout the book. +This section lists all the syntax valid in patterns and why you might want to +use each of them. + +### Literals + +As we saw in Chapter 6, you can match against literals directly: + +```rust +let x = 1; + +match x { + 1 => println!("one"), + 2 => println!("two"), + 3 => println!("three"), + _ => println!("anything"), +} +``` + +This prints `one` since the value in `x` is 1. + +### Named Variables + +Named variables are irrefutable patterns that match any value. + +As with all variables, variables declared as part of a pattern will shadow +variables with the same name outside of the `match` construct since a `match` +starts a new scope. In Listing 18-10, we declare a variable named `x` with the +value `Some(5)` and a variable `y` with the value `10`. Then we have a `match` +expression on the value `x`. Take a look at the patterns in the match arms and +the `println!` at the end, and make a guess about what will be printed before +running this code or reading further: + +Filename: src/main.rs + +```rust +fn main() { + 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 = {:?}, y = {:?}", x, y); +} +``` + +Listing 18-10: A `match` statement with an arm that +introduces a shadowed variable `y` + +Let's walk through what happens when the `match` statement runs. The first +match arm has the pattern `Some(50)`, and the value in `x` (`Some(5)`) does not +match `Some(50)`, so we continue. In the second match arm, the pattern +`Some(y)` introduces a new variable name `y` that will match any value inside a +`Some` value. Because we're in a new scope inside the `match` expression, this +is a new variable, not the `y` we declared at the beginning that has the value +10. The new `y` binding will match any value inside a `Some`, which is what we +have in `x`, so we execute the expression for that arm and print `Matched, y = +5` since this `y` binds to the inner value of the `Some` in `x`, which is 5. + +If `x` had been a `None` value instead of `Some(5)`, we would have matched the +underscore since the other two arms' patterns would not have matched. In the +expression for that match arm, since we did not introduce an `x` variable in +the pattern of the arm, this `x` is still the outer `x` that has not been +shadowed. In this hypothetical case, the `match` would print `Default case, x = +None`. + +Once the `match` expression is over, its scope ends, and so does the scope of +the inner `y`. The last `println!` produces `at the end: x = Some(5), y = 10`. + +In order to make a `match` expression that compares the values of the outer `x` +and `y` rather than introducing a shadowed variable, we would need to use a +match guard conditional instead. We'll be talking about match guards later in +this section. + +### Multiple patterns + +You can match multiple patterns with `|`, which means *or*: + +```rust +let x = 1; + +match x { + 1 | 2 => println!("one or two"), + 3 => println!("three"), + _ => println!("anything"), +} +``` + +This prints `one or two`. + +### Matching Ranges of Values with `...` + +You can match a range of values with `...`: + +```rust +let x = 5; + +match x { + 1 ... 5 => println!("one through five"), + _ => println!("something else"), +} +``` + +Ranges are only allowed with numeric values or `char` values. Here's an example +using ranges of `char` values: + +```rust +let x = 'c'; + +match x { + 'a' ... 'j' => println!("early ASCII letter"), + 'k' ... 'z' => println!("late ASCII letter"), + _ => println!("something else"), +} +``` + +This will print `early ASCII letter`. + +### Destructuring to Break Apart Values + +Patterns can be used to *destructure* structs, enums, tuples, and references. +Destructuring means to break a value up into its component pieces. Listing +18-11 shows a `Point` struct with two fields, `x` and `y`, that we can break +apart by using a pattern with a `let` statement: + +Filename: src/main.rs + +```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); +} +``` + +Listing 18-11: Destructuring using struct field +shorthand + +This creates the variables `x` and `y` that match the `x` and `y` of `p`. The +names of the variables must match the names of the fields to use this +shorthand. If we wanted to use names different than the variable names, we can +specify `field_name: variable_name` in the pattern. In Listing 18-12, `a` will +have the value in the `Point` instance's `x` field and `b` will have the value +in the `y` field: + +Filename: src/main.rs + +```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); +} +``` + +Listing 18-12: Destructuring struct fields into variables +with different names than the fields + +We can also use destructuring with literal values in order to test and use +inner parts of a value. Listing 18-13 shows a `match` statement that determines +whether a point lies directly on the `x` axis (which is true when `y = 0`), on +the `y` axis (`x = 0`), or neither: + +```rust +# struct Point { +# x: i32, +# y: i32, +# } +# +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), + } +} +``` + +Listing 18-13: Destructuring and matching literal values +in one pattern + +This will print `On the y axis at 7` since the value `p` matches the second arm +by virtue of `x` having the value 0. + +We used desctructuring on enums in Chapter 6, such as in Listing 6-5 where we +destructured an `Option` using a `match` expression and added one to the +inner value of the `Some` variant. + +When the value we're matching against a pattern contains a reference, we can +specify a `&` in the pattern in order to separate the reference and the value. +This is especially useful in closures used with iterators that iterate over +references to values when we want to use the values in the closure rather than +the references. Listing 18-14 shows how to iterate over references to `Point` +instances in a vector, and destructure both the reference and the struct in +order to be able to perform calculations on the `x` and `y` values easily: + +```rust +# struct Point { +# x: i32, +# y: i32, +# } +# +let points = vec![ + Point { x: 0, y: 0}, + Point { x: 1, y: 5}, + Point { x: 10, y: -3}, +]; +let sum_of_squares: i32 = points + .iter() + .map(|&Point {x, y}| x * x + y * y) + .sum(); +``` + +Listing 18-14: Destructuring a reference to a struct into +the struct field values + +Because `iter` iterates over references to the items in the vector, if we +forgot the `&` in the closure arguments in the `map`, we'd get a type mismatch +error like this: + +```text +error[E0308]: mismatched types + --> + | +14 | .map(|Point {x, y}| x * x + y * y) + | ^^^^^^^^^^^^ expected &Point, found struct `Point` + | + = note: expected type `&Point` + found type `Point` +``` + +This says Rust was expecting our closure to match `&Point`, but we tried to +match the value with a pattern that was a `Point` value, not a reference to a +`Point`. + +We can mix, match, and nest destructuring patterns in even more complex ways: +we can do something complicated like this example where we nest structs and and +tuples inside of a tuple and destructure all the primitive values out: + +```rust +# struct Point { +# x: i32, +# y: i32, +# } +# +let ((feet, inches), Point {x, y}) = ((3, 10), Point { x: 3, y: -10 }); +``` + +This lets us break complex types into their component parts. + +### Ignoring Values in a Pattern + +There are a few ways to ignore entire values or parts of values: using the `_` +pattern, using the `_` pattern within another pattern, using a name that starts +with an underscore, or using `..` to ignore all remaining parts of a value. +Let's explore how and why to do each of these. + +#### Ignoring an Entire Value with `_` + +We've seen the use of underscore as a wildcard pattern that will match any value +but not bind to the value. While the underscore pattern is especially useful as +the last arm in a `match` expression, we can use it in any pattern, such as +function arguments as shown in Listing 18-15: + +```rust +fn foo(_: i32) { + // code goes here +} +``` + +Listing 18-15: Using `_` in a function signature + +Normally, you would change the signature to not have the unused parameter. In +cases such as implementing a trait, where you need a certain type signature, +using an underscore lets you ignore a parameter, and the compiler won't warn +about unused function parameters like it would if we had used a name instead. + +#### Ignoring Parts of a Value with a Nested `_` + +We can also use `_` inside of another pattern to ignore just part of a value. +In Listing 18-16, the first `match` arm's pattern matches a `Some` value but +ignores the value inside of the `Some` variant as specified by the underscore: + +```rust +let x = Some(5); + +match x { + Some(_) => println!("got a Some and I don't care what's inside"), + None => (), +} +``` + +Listing 18-16: Ignoring the value inside of the `Some` +variant by using a nested underscore + +This is useful when the code associated with the `match` arm doesn't use the +nested part of the variable at all. + +We can also use underscores in multiple places within one pattern, as shown in +Listing 18-17 where we're ignoring the second and fourth values in a tuple of +five items: + +```rust +let numbers = (2, 4, 8, 16, 32); + +match numbers { + (first, _, third, _, fifth) => { + println!("Some numbers: {}, {}, {}", first, third, fifth) + }, +} +``` + +Listing 18-17: Ignoring multiple parts of a tuple + +This will print `Some numbers: 2, 8, 32`, and the values 4 and 16 will be +ignored. + +#### Ignoring an Unused Variable by Starting its Name with an Underscore + +Usually, Rust will warn you if you create a variable but don't use it anywhere, +since that could be a bug. If you're prototyping or just starting a project, +though, you might create a variable that you'll use eventually, but temporarily +it will be unused. If you're in this situation and would like to tell Rust not +to warn you about the unused variable, you can start the name of the variable +with an underscore. This works just like a variable name in any pattern, only +Rust won't warn you if the variable goes unused. In Listing 18-18, we +do get a warning about not using the variable `y`, but we don't get a warning +about not using the variable `_x`: + +```rust +fn main() { + let _x = 5; + let y = 10; +} +``` + +Listing 18-18: Starting a variable name with an underscore +in order to not get unused variable warnings + +Note that there is a subtle difference between using only `_` and using a name +that starts with an underscore like `_x`: `_x` still binds the value to the +variable, but `_` doesn't bind at all. + +Listing 18-19 shows a case where this distinction matters: `s` will still be +moved into `_s`, which prevents us from using `s` again: + +```rust,ignore +let s = String::from("Hello!"); + +let _s = s; + +println!("{}", s); +``` + +Listing 18-19: An unused variable starting with an +underscore still binds the value, which may take ownership of the value + +Using underscore by itself, however, doesn't ever bind to the value. Listing +18-20 will compile without any errors since `s` does not get moved into `_`: + +```rust +let s = String::from("Hello!"); + +let _ = s; + +println!("{}", s); +``` + +Listing 18-20: Using underscore does not bind the +value + +This works just fine. Because we never bind `s` to anything, it's not moved. + +### Ignoring Remaining Parts of a Value with `..` + +With values that have many parts, we can extract only a few parts and avoid +having to list underscores for each remaining part by instead using `..`. The +`..` pattern will ignore any parts of a value that we haven't explicitly +matched in the rest of the pattern. In Listing 18-21, we have a `Point` struct +that holds a coordinate in three dimensional space. In the `match` expression, +we only want to operate on the `x` coordinate and ignore the values in the `y` +and `z` fields: + +```rust +struct Point { + x: i32, + y: i32, + z: i32, +} + +let origin = Point { x: 0, y: 0, z: 0 }; + +match origin { + Point { x, .. } => println!("x is {}", x), +} +``` + +Listing 18-21: Ignoring all fields of a `Point` except +for `x` by using `..` + +Using `..` is shorter to type than having to list out `_y` and `_z`. The `..` +pattern is especially useful when working with structs that have lots of fields +in situations where only one or two fields are relevant. + +`..` will expand to as many values as it needs to be. Listing 18-22 shows a use +of `..` with a tuple: + +```rust +fn main() { + let numbers = (2, 4, 8, 16, 32); + + match numbers { + (first, .., last) => { + println!("Some numbers: {}, {}", first, last); + }, + } +} +``` + +Listing 18-22: Matching only the first and last values in +a tuple and ignoring all other values with `..` + +Here, we have the first and last value matched, with `first` and `last`. The +`..` will match and ignore all of the things in the middle. + +Using `..` must be unambiguous, however. Listing 18-23 shows an example where +it's not clear to Rust which values we want to match and which values we want +to ignore: + +```rust,ignore +fn main() { + let numbers = (2, 4, 8, 16, 32); + + match numbers { + (.., second, ..) => { + println!("Some numbers: {}", second) + }, + } +} +``` + +Listing 18-23: An attempt to use `..` in a way that is +ambiguous + +If we compile this example, we get this error: + +```text +error: `..` can only be used once per tuple or tuple struct pattern + --> src/main.rs:5:22 + | +5 | (.., second, ..) => { + | ^^ +``` + +It's not possible to determine how many values in the tuple should be ignored +before one value is matched with `second`, and then how many further values are +ignored after that. We could mean that we want to ignore 2, bind `second` to 4, +then ignore 8, 16, and 32, or we could mean that we want to ignore 2 and 4, +bind `second` to 8, then ignore 16 and 32, and so forth. The variable name +`second` doesn't mean anything special to Rust, so we get a compiler error +since using `..` in two places like this is ambiguous. + +### `ref` and `ref mut` to Create References in Patterns + +Usually, when you match against a pattern, the variables that the pattern +introduces are bound to a value. This means you'll end up moving the value into +the `match` (or wherever you're using the pattern) since the ownership rules +apply. Listing 18-24 shows an example: + +```rust,ignore +let robot_name = Some(String::from("Bors")); + +match robot_name { + Some(name) => println!("Found a name: {}", name), + None => (), +} + +println!("robot_name is: {:?}", robot_name); +``` + +Listing 18-24: Creating a variable in a match arm pattern +takes ownership of the value + +This example will fail to compile since the value inside the `Some` value in +`robot_name` is moved within the `match` when `name` binds to that value. + +Using `&` in a pattern matches an existing reference in the value, as we saw in +the previous section on destructuring. If you want to create a reference +instead in order to borrow the value in a pattern variable, use the `ref` +keyword before the new variable, as shown in Listing 18-25: + +```rust +let robot_name = Some(String::from("Bors")); + +match robot_name { + Some(ref name) => println!("Found a name: {}", name), + None => (), +} + +println!("robot_name is: {:?}", robot_name); +``` + +Listing 18-25: Creating a reference so that a pattern +variable does not take ownership of a value + +This example will compile because the value in the `Some` variant in +`robot_name` is not moved into the `Some(ref name)` arm of the match; the match +only took a reference to the data in `robot_name` rather than moving it. + +To create a mutable reference, use `ref mut` for the same reason as shown in +Listing 18-26: + +```rust +let mut robot_name = Some(String::from("Bors")); + +match robot_name { + Some(ref mut name) => *name = String::from("Another name"), + None => (), +} + +println!("robot_name is: {:?}", robot_name); +``` + +Listing 18-26: Creating a mutable reference to a value as +part of a pattern using `ref mut` + +This example will compile and print `robot_name is: Some("Another name")`. +Since `name` is a mutable reference, within the match arm code, we need to +dereference using the `*` operator in order to be able to mutate the value. + +### Extra Conditionals with Match Guards + +You can introduce *match guards* as part of a match arm by specifying an +additional `if` conditional after the pattern. The conditional can use +variables created in the pattern. Listing 18-27 has a `match` expression with a +match guard in the first arm: + +```rust +let num = Some(4); + +match num { + Some(x) if x < 5 => println!("less than five: {}", x), + Some(x) => println!("{}", x), + None => (), +} +``` + +Listing 18-27: Adding a match guard to a pattern + +This example will print `less than five: 4`. If `num` was instead `Some(7)`, +this example would print `7`. Match guards allow you to express more complexity +than patterns alone give you. + +In Listing 18-10, we saw that since patterns shadow variables, we weren't able +to specify a pattern to express the case when a value was equal to a variable +outside the `match`. Listing 18-28 shows how we can use a match guard to +accomplish this: + +```rust +fn main() { + let x = Some(5); + let y = 10; + + match x { + Some(50) => println!("Got 50"), + Some(n) if n == y => println!("Matched, n = {:?}", n), + _ => println!("Default case, x = {:?}", x), + } + + println!("at the end: x = {:?}, y = {:?}", x, y); +} +``` + +Listing 18-28: Using a match guard to test for equality +with an outer variable + +This will now print `Default case, x = Some(5)`. Because the second match arm +is not introducing a new variable `y` that shadows the outer `y` in the +pattern, we can use `y` in the match guard. We're still destructuring `x` to +get the inner value `n`, and then we can compare `n` and `y` in the match guard. + +If you're using a match guard with multiple patterns specified by `|`, the +match guard condition applies to all of the patterns. Listing 18-29 shows a +match guard that applies to the value matched by all three patterns in the +first arm: + +```rust +let x = 4; +let y = false; + +match x { + 4 | 5 | 6 if y => println!("yes"), + _ => println!("no"), +} +``` + +Listing 18-29: Combining multiple patterns with a match +guard + +This prints `no` since the `if` condition applies to the whole pattern `4 | 5 | +6`, not only to the last value `6`. In other words, the precedence of a match +guard in relation to a pattern behaves like this: + +```text +(4 | 5 | 6) if y => ... +``` + +rather than this: + +```text +4 | 5 | (6 if y) => ... +``` + +### `@` Bindings + +In order to test a value in a pattern but also be able to create a variable +bound to the value, we can use `@`. Listing 18-30 shows an example where we +want to test that a `Message::Hello` `id` field is within the range `3...7` but +also be able to bind to the value so that we can use it in the code associated +with the arm: + +```rust +enum Message { + Hello { id: i32 }, +} + +let msg = Message::Hello { id: 5 }; + +match msg { + Message::Hello { id: id @ 3...7 } => { + println!("Found an id in range: {}", id) + }, + Message::Hello { id: 10...12 } => { + println!("Found an id in another range") + }, + Message::Hello { id } => { + println!("Found some other id: {}", id) + }, +} +``` + +Listing 18-30: Using `@` to bind to a value in a pattern +while also testing it + +This example will print `Found an id in range: 5`. By specifying `id @` before +the range, we're capturing whatever value matched the range while also testing +it. In the second arm where we only have a range specified in the pattern, the +code associated with the arm doesn't know if `id` is 10, 11, or 12, since we +haven't saved the `id` value in a variable: we only know that the value matched +something in that range if that arm's code is executed. In the last arm where +we've specified a variable without a range, we do have the value available to +use in the arm's code, but we haven't applied any other test to the value. +Using `@` lets us test a value and save it in a variable within one pattern. + +## Summary + +Patterns are a useful feature of Rust that help to distinguish between +different kinds of data. When used in `match` statements, Rust makes sure that +your patterns cover every possible value. Patterns in `let` statements and +function parameters make those constructs more powerful, enabling the +destructuring of values into smaller parts at the same time as assigning to +variables. + +Now, for the penultimate chapter of the book, let's take a look at some +advanced parts of a variety of Rust's features. From 3e59e17a0a97dc1c8269fa8a852d591c78d96d38 Mon Sep 17 00:00:00 2001 From: "Carol (Nichols || Goulding)" Date: Fri, 7 Apr 2017 20:01:50 -0400 Subject: [PATCH 20/22] Fix spelling --- second-edition/dictionary.txt | 2 ++ second-edition/src/ch18-01-all-the-places-for-patterns.md | 2 +- second-edition/src/ch18-03-pattern-syntax.md | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/second-edition/dictionary.txt b/second-edition/dictionary.txt index fdab46a492..06c4ecf4d1 100644 --- a/second-edition/dictionary.txt +++ b/second-edition/dictionary.txt @@ -70,6 +70,7 @@ dereferencing DerefMut destructor destructure +destructured destructures destructuring Destructuring @@ -299,6 +300,7 @@ tradeoff tradeoffs TrafficLight trpl +tuesday tuple tuples typeof diff --git a/second-edition/src/ch18-01-all-the-places-for-patterns.md b/second-edition/src/ch18-01-all-the-places-for-patterns.md index de5aac57ff..34dd2f001b 100644 --- a/second-edition/src/ch18-01-all-the-places-for-patterns.md +++ b/second-edition/src/ch18-01-all-the-places-for-patterns.md @@ -42,7 +42,7 @@ if the pattern in the `if let` doesn't match. Listing 18-1 shows that it's even possible to mix and match `if let`, `else if`, and `else if let`. This code shows a series of checks of a bunch of different conditions to decide what the background color should be. For the -purposes of the examplee, we've created variables with hardcoded values that a +purposes of the example, we've created variables with hardcoded values that a real program might get by asking the user. If the user has specified a favorite color, we'll use that as the background color. If today is Tuesday, the background color will be green. If the user has specified their age as a string diff --git a/second-edition/src/ch18-03-pattern-syntax.md b/second-edition/src/ch18-03-pattern-syntax.md index cf7b4e958c..ef263c3ac5 100644 --- a/second-edition/src/ch18-03-pattern-syntax.md +++ b/second-edition/src/ch18-03-pattern-syntax.md @@ -204,7 +204,7 @@ in one pattern This will print `On the y axis at 7` since the value `p` matches the second arm by virtue of `x` having the value 0. -We used desctructuring on enums in Chapter 6, such as in Listing 6-5 where we +We used destructuring on enums in Chapter 6, such as in Listing 6-5 where we destructured an `Option` using a `match` expression and added one to the inner value of the `Some` variant. From 5d794b471188ff7560c114cade5cb60a7f94c92c Mon Sep 17 00:00:00 2001 From: "Carol (Nichols || Goulding)" Date: Mon, 10 Apr 2017 10:14:29 -0400 Subject: [PATCH 21/22] Fixing things matthewjasper pointed out --- second-edition/src/ch18-02-refutability.md | 7 ++- second-edition/src/ch18-03-pattern-syntax.md | 56 ++++++++++++-------- 2 files changed, 36 insertions(+), 27 deletions(-) diff --git a/second-edition/src/ch18-02-refutability.md b/second-edition/src/ch18-02-refutability.md index 17669c5e08..b0de108c1f 100644 --- a/second-edition/src/ch18-02-refutability.md +++ b/second-edition/src/ch18-02-refutability.md @@ -5,10 +5,9 @@ fail to match for any possible value are *irrefutable*, and patterns which can fail to match for some possible value are said to be *refutable*. `let` statements, function parameters, and `for` loops are restricted to only accept irrefutable patterns, since there's nothing correct the program could do if the -pattern fails to match. `match`, `if let`, and `while let` expressions are -restricted to only accept refutable patterns, since they're made to handle -possible failure and we wouldn't need their functionality if the pattern could -never fail. +pattern fails to match. `if let`, and `while let` expressions are restricted to +only accept refutable patterns, since they're made to handle possible failure +and we wouldn't need their functionality if the pattern could never fail. In general, you shouldn't have to worry about the distinction between refutable and irrefutable patterns; just be familiar with the concept of refutability diff --git a/second-edition/src/ch18-03-pattern-syntax.md b/second-edition/src/ch18-03-pattern-syntax.md index ef263c3ac5..c57871f65e 100644 --- a/second-edition/src/ch18-03-pattern-syntax.md +++ b/second-edition/src/ch18-03-pattern-syntax.md @@ -53,15 +53,18 @@ fn main() { Listing 18-10: A `match` statement with an arm that introduces a shadowed variable `y` + + Let's walk through what happens when the `match` statement runs. The first match arm has the pattern `Some(50)`, and the value in `x` (`Some(5)`) does not match `Some(50)`, so we continue. In the second match arm, the pattern `Some(y)` introduces a new variable name `y` that will match any value inside a `Some` value. Because we're in a new scope inside the `match` expression, this -is a new variable, not the `y` we declared at the beginning that has the value -10. The new `y` binding will match any value inside a `Some`, which is what we -have in `x`, so we execute the expression for that arm and print `Matched, y = -5` since this `y` binds to the inner value of the `Some` in `x`, which is 5. +is a new variable, not the `y` we declared at the beginning that has the +value 10. The new `y` binding will match any value inside a `Some`, which is +what we have in `x`, so we execute the expression for that arm and print +`Matched, y = 5` since this `y` binds to the inner value of the `Some` in `x`, +which is 5. If `x` had been a `None` value instead of `Some(5)`, we would have matched the underscore since the other two arms' patterns would not have matched. In the @@ -80,7 +83,8 @@ this section. ### Multiple patterns -You can match multiple patterns with `|`, which means *or*: +In `match` expressions only, you can match multiple patterns with `|`, which +means *or*: ```rust let x = 1; @@ -96,7 +100,7 @@ This prints `one or two`. ### Matching Ranges of Values with `...` -You can match a range of values with `...`: +You can match an inclusive range of values with `...`: ```rust let x = 5; @@ -107,6 +111,8 @@ match x { } ``` +If `x` is 1, 2, 3, 4, or 5, the first arm will match. + Ranges are only allowed with numeric values or `char` values. Here's an example using ranges of `char` values: @@ -192,7 +198,7 @@ fn main() { 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: 0, y } => println!("On the y axis at {}", y), Point { x, y } => println!("On neither axis: ({}, {})", x, y), } } @@ -223,9 +229,9 @@ order to be able to perform calculations on the `x` and `y` values easily: # } # let points = vec![ - Point { x: 0, y: 0}, - Point { x: 1, y: 5}, - Point { x: 10, y: -3}, + Point { x: 0, y: 0 }, + Point { x: 1, y: 5 }, + Point { x: 10, y: -3 }, ]; let sum_of_squares: i32 = points .iter() @@ -367,11 +373,13 @@ Listing 18-19 shows a case where this distinction matters: `s` will still be moved into `_s`, which prevents us from using `s` again: ```rust,ignore -let s = String::from("Hello!"); +let s = Some(String::from("Hello!")); -let _s = s; +if let Some(_s) = s { + println!("found a string"); +} -println!("{}", s); +println!("{:?}", s); ``` Listing 18-19: An unused variable starting with an @@ -381,11 +389,13 @@ Using underscore by itself, however, doesn't ever bind to the value. Listing 18-20 will compile without any errors since `s` does not get moved into `_`: ```rust -let s = String::from("Hello!"); +let s = Some(String::from("Hello!")); -let _ = s; +if let Some(_) = s { + println!("found a string"); +} -println!("{}", s); +println!("{:?}", s); ``` Listing 18-20: Using underscore does not bind the @@ -393,7 +403,7 @@ value This works just fine. Because we never bind `s` to anything, it's not moved. -### Ignoring Remaining Parts of a Value with `..` +#### Ignoring Remaining Parts of a Value with `..` With values that have many parts, we can extract only a few parts and avoid having to list underscores for each remaining part by instead using `..`. The @@ -420,9 +430,9 @@ match origin { Listing 18-21: Ignoring all fields of a `Point` except for `x` by using `..` -Using `..` is shorter to type than having to list out `_y` and `_z`. The `..` -pattern is especially useful when working with structs that have lots of fields -in situations where only one or two fields are relevant. +Using `..` is shorter to type than having to list out `y: _` and `z: _`. The +`..` pattern is especially useful when working with structs that have lots of +fields in situations where only one or two fields are relevant. `..` will expand to as many values as it needs to be. Listing 18-22 shows a use of `..` with a tuple: @@ -507,9 +517,9 @@ This example will fail to compile since the value inside the `Some` value in `robot_name` is moved within the `match` when `name` binds to that value. Using `&` in a pattern matches an existing reference in the value, as we saw in -the previous section on destructuring. If you want to create a reference -instead in order to borrow the value in a pattern variable, use the `ref` -keyword before the new variable, as shown in Listing 18-25: +the "Destructuring to Break Apart Values" section. If you want to create a +reference instead in order to borrow the value in a pattern variable, use the +`ref` keyword before the new variable, as shown in Listing 18-25: ```rust let robot_name = Some(String::from("Bors")); From 4ca9e513e532a4d229ab5af7dfcc567129623bf4 Mon Sep 17 00:00:00 2001 From: "Carol (Nichols || Goulding)" Date: Mon, 10 Apr 2017 11:23:21 -0400 Subject: [PATCH 22/22] Fix steve nits --- .../src/ch18-01-all-the-places-for-patterns.md | 4 ++-- second-edition/src/ch18-02-refutability.md | 15 ++++++++------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/second-edition/src/ch18-01-all-the-places-for-patterns.md b/second-edition/src/ch18-01-all-the-places-for-patterns.md index 34dd2f001b..61b15adb15 100644 --- a/second-edition/src/ch18-01-all-the-places-for-patterns.md +++ b/second-edition/src/ch18-01-all-the-places-for-patterns.md @@ -153,7 +153,7 @@ This will print: The first call to `enumerate` produces the tuple `(0, 1)`. When this value is matched to the pattern `(index, value)`, `index` will be 0 and `value` will -equal 1. +be 1. ### `let` Statements @@ -178,7 +178,7 @@ slot; a variable name is just a particularly humble form of pattern. With `let`, we compare the expression against the pattern, and assign any names we find. So for example, in our `let x = 5;` case, `x` is a pattern that says -"bind what matches here to the variable `x`. And since the name `x` is the +"bind what matches here to the variable `x`." And since the name `x` is the whole pattern, this pattern effectively means "bind everything to the variable `x`, whatever the value is." diff --git a/second-edition/src/ch18-02-refutability.md b/second-edition/src/ch18-02-refutability.md index b0de108c1f..30eac1eada 100644 --- a/second-edition/src/ch18-02-refutability.md +++ b/second-edition/src/ch18-02-refutability.md @@ -1,13 +1,14 @@ ## Refutability: Whether a Pattern Might Fail to Match Patterns come in two forms: refutable and irrefutable. Patterns which cannot -fail to match for any possible value are *irrefutable*, and patterns which can -fail to match for some possible value are said to be *refutable*. `let` -statements, function parameters, and `for` loops are restricted to only accept -irrefutable patterns, since there's nothing correct the program could do if the -pattern fails to match. `if let`, and `while let` expressions are restricted to -only accept refutable patterns, since they're made to handle possible failure -and we wouldn't need their functionality if the pattern could never fail. +fail to match for any possible value are said to be *irrefutable*, and patterns +which can fail to match for some possible value are said to be *refutable*. +`let` statements, function parameters, and `for` loops are restricted to only +accept irrefutable patterns, since there's nothing correct the program could do +if the pattern fails to match. `if let`, and `while let` expressions are +restricted to only accept refutable patterns, since they're made to handle +possible failure and we wouldn't need their functionality if the pattern could +never fail. In general, you shouldn't have to worry about the distinction between refutable and irrefutable patterns; just be familiar with the concept of refutability