From c433926ea36b766a17c24c347461d738f493f145 Mon Sep 17 00:00:00 2001 From: Zheng Ping Date: Mon, 26 Jun 2017 11:08:02 +0800 Subject: [PATCH] Pattern syntax. --- ch18-03-pattern-syntax.md | 697 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 697 insertions(+) create mode 100644 ch18-03-pattern-syntax.md diff --git a/ch18-03-pattern-syntax.md b/ch18-03-pattern-syntax.md new file mode 100644 index 0000000..795e221 --- /dev/null +++ b/ch18-03-pattern-syntax.md @@ -0,0 +1,697 @@ +## 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 + +In `match` expressions only, 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 an inclusive range of values with `...`: + +```rust +let x = 5; + +match x { + 1 ... 5 => println!("one through five"), + _ => println!("something else"), +} +``` + +If `x` is 1, 2, 3, 4, or 5, the first arm will match. + +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 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. + +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 +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 = Some(String::from("Hello!")); + +if let Some(_s) = s { + println!("found a string"); +} + +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 = Some(String::from("Hello!")); + +if let Some(_) = s { + println!("found a string"); +} + +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 "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")); + +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.