Skip to content

Commit 88dffa2

Browse files
committed
update book for match bindings
Fixes #1288
1 parent 01d48dd commit 88dffa2

File tree

3 files changed

+108
-124
lines changed

3 files changed

+108
-124
lines changed

2018-edition/src/ch09-02-recoverable-errors-with-result.md

Lines changed: 39 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -157,22 +157,12 @@ fn main() {
157157
158158
let f = match f {
159159
Ok(file) => file,
160-
Err(ref error) if error.kind() == ErrorKind::NotFound => {
161-
match File::create("hello.txt") {
160+
Err(error) => match error.kind() {
161+
ErrorKind::NotFound => match File::create("hello.txt") {
162162
Ok(fc) => fc,
163-
Err(e) => {
164-
panic!(
165-
"Tried to create file but there was a problem: {:?}",
166-
e
167-
)
168-
},
169-
}
170-
},
171-
Err(error) => {
172-
panic!(
173-
"There was a problem opening the file: {:?}",
174-
error
175-
)
163+
Err(e) => panic!("Tried to create file but there was a problem: {:?}", e),
164+
},
165+
other_error => panic!("There was a problem opening the file: {:?}", other_error),
176166
},
177167
};
178168
}
@@ -187,25 +177,44 @@ has a method `kind` that we can call to get an `io::ErrorKind` value. The enum
187177
`io::ErrorKind` is provided by the standard library and has variants
188178
representing the different kinds of errors that might result from an `io`
189179
operation. The variant we want to use is `ErrorKind::NotFound`, which indicates
190-
the file we’re trying to open doesn’t exist yet.
191-
192-
The condition `if error.kind() == ErrorKind::NotFound` is called a *match
193-
guard*: it’s an extra condition on a `match` arm that further refines the arm’s
194-
pattern. This condition must be true for that arm’s code to be run; otherwise,
195-
the pattern matching will move on to consider the next arm in the `match`. The
196-
`ref` in the pattern is needed so `error` is not moved into the guard condition
197-
but is merely referenced by it. The reason you use `ref` to create a reference
198-
in a pattern instead of `&` will be covered in detail in Chapter 18. In short,
199-
in the context of a pattern, `&` matches a reference and gives you its value,
200-
but `ref` matches a value and gives you a reference to it.
180+
the file we’re trying to open doesn’t exist yet. So, we `match` on `f`, but we
181+
also then have an inner `match` on `error.kind()`.
201182

202183
The condition we want to check in the match guard is whether the value returned
203184
by `error.kind()` is the `NotFound` variant of the `ErrorKind` enum. If it is,
204185
we try to create the file with `File::create`. However, because `File::create`
205-
could also fail, we need to add an inner `match` statement as well. When the
206-
file can’t be opened, a different error message will be printed. The last arm
207-
of the outer `match` stays the same so the program panics on any error besides
208-
the missing file error.
186+
could also fail, we need to add another inner `match` statement as well. When
187+
the file can’t be opened, a different error message will be printed. The last
188+
arm of the outer `match` stays the same so the program panics on any error
189+
besides the missing file error.
190+
191+
That's a lot of `match`! `match` is very powerful, but also very much a primitive.
192+
In Chapter 13, we'll learn about closures. The `Result<T, E>` type has many
193+
methods that accept a closure, and are implemented as `match` statements. A more
194+
seasoned Rustacean might write this:
195+
196+
```rust
197+
use std::fs::File;
198+
use std::io::ErrorKind;
199+
200+
fn main() {
201+
let f = File::open("hello.txt").map_err(|error| {
202+
if error.kind() == ErrorKind::NotFound {
203+
File::create("hello.txt").unwrap_or_else(|error| {
204+
panic!("Tried to create file but there was a problem: {:?}", error);
205+
})
206+
} else {
207+
panic!("There was a problem opening the file: {:?}", error);
208+
}
209+
});
210+
}
211+
```
212+
213+
Come back to this example after you've read Chapter 13, and look up what the
214+
`map_err` and `unwrap_or_else` methods do in the standard library
215+
documentation. There's many more of these methods that can clean up huge
216+
nested `match`es when dealing with errors. We'll be looking at some other
217+
strategies shortly!
209218

210219
### Shortcuts for Panic on Error: `unwrap` and `expect`
211220

2018-edition/src/ch15-06-reference-cycles.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@ enum List {
3434

3535
impl List {
3636
fn tail(&self) -> Option<&RefCell<Rc<List>>> {
37-
match *self {
38-
Cons(_, ref item) => Some(item),
37+
match self {
38+
Cons(_, item) => Some(item),
3939
Nil => None,
4040
}
4141
}
@@ -72,8 +72,8 @@ reference counts are at various points in this process.
7272
#
7373
# impl List {
7474
# fn tail(&self) -> Option<&RefCell<Rc<List>>> {
75-
# match *self {
76-
# Cons(_, ref item) => Some(item),
75+
# match self {
76+
# Cons(_, item) => Some(item),
7777
# Nil => None,
7878
# }
7979
# }

2018-edition/src/ch18-03-pattern-syntax.md

Lines changed: 65 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -707,99 +707,14 @@ ignore thereafter. This code could mean that we want to ignore `2`, bind
707707
The variable name `second` doesn’t mean anything special to Rust, so we get a
708708
compiler error because using `..` in two places like this is ambiguous.
709709

710-
### Creating References in Patterns with `ref` and `ref mut`
711-
712-
Let’s look at using `ref` to make references so ownership of the values isn’t
713-
moved to variables in the pattern. Usually, when you match against a pattern,
714-
the variables introduced by the pattern are bound to a value. Rust’s ownership
715-
rules mean the value will be moved into the `match` or wherever you’re using
716-
the pattern. Listing 18-26 shows an example of a `match` that has a pattern
717-
with a variable and then usage of the entire value in the `println!` statement
718-
later, after the `match`. This code will fail to compile because ownership of
719-
part of the `robot_name` value is transferred to the `name` variable in the
720-
pattern of the first `match` arm.
721-
722-
```rust,ignore
723-
let robot_name = Some(String::from("Bors"));
724-
725-
match robot_name {
726-
Some(name) => println!("Found a name: {}", name),
727-
None => (),
728-
}
729-
730-
println!("robot_name is: {:?}", robot_name);
731-
```
732-
733-
<span class="caption">Listing 18-26: Creating a variable in a `match` arm
734-
pattern takes ownership of the value</span>
735-
736-
Because ownership of part of `robot_name` has been moved to `name`, we can no
737-
longer use `robot_name` in the `println!` after the `match` because
738-
`robot_name` no longer has ownership.
739-
740-
To fix this code, we want to make the `Some(name)` pattern *borrow* that part
741-
of `robot_name` rather than taking ownership. You’ve already seen that, outside
742-
of patterns, the way to borrow a value is to create a reference using `&`, so
743-
you might think the solution is changing `Some(name)` to `Some(&name)`.
744-
745-
However, as you saw in the “Destructuring to Break Apart Values” section, the
746-
syntax `&` in patterns does not *create* a reference but *matches* an existing
747-
reference in the value. Because `&` already has that meaning in patterns, we
748-
can’t use `&` to create a reference in a pattern.
749-
750-
Instead, to create a reference in a pattern, we use the `ref` keyword before
751-
the new variable, as shown in Listing 18-27.
752-
753-
```rust
754-
let robot_name = Some(String::from("Bors"));
755-
756-
match robot_name {
757-
Some(ref name) => println!("Found a name: {}", name),
758-
None => (),
759-
}
760-
761-
println!("robot_name is: {:?}", robot_name);
762-
```
763-
764-
<span class="caption">Listing 18-27: Creating a reference so a pattern variable
765-
does not take ownership of a value</span>
766-
767-
This example will compile because the value in the `Some` variant in
768-
`robot_name` is not moved into the `match`; the `match` only took a reference
769-
to the data in `robot_name` rather than moving it.
770-
771-
To create a mutable reference so we’re able to mutate a value matched in a
772-
pattern, we use `ref mut` instead of `&mut`. The reason is, again, that in
773-
patterns, the latter is for matching existing mutable references, not creating
774-
new ones. Listing 18-28 shows an example of a pattern creating a mutable
775-
reference.
776-
777-
```rust
778-
let mut robot_name = Some(String::from("Bors"));
779-
780-
match robot_name {
781-
Some(ref mut name) => *name = String::from("Another name"),
782-
None => (),
783-
}
784-
785-
println!("robot_name is: {:?}", robot_name);
786-
```
787-
788-
<span class="caption">Listing 18-28: Creating a mutable reference to a value as
789-
part of a pattern using `ref mut`</span>
790-
791-
This example will compile and print `robot_name is: Some("Another name")`.
792-
Because `name` is a mutable reference, we need to dereference within the match
793-
arm code using the `*` operator to mutate the value.
794-
795710
### Extra Conditionals with Match Guards
796711

797712
A *match guard* is an additional `if` condition specified after the pattern in
798713
a `match` arm that must also match, along with the pattern matching, for that
799714
arm to be chosen. Match guards are useful for expressing more complex ideas
800715
than a pattern alone allows.
801716

802-
The condition can use variables created in the pattern. Listing 18-29 shows a
717+
The condition can use variables created in the pattern. Listing 18-26 shows a
803718
`match` where the first arm has the pattern `Some(x)` and also has a match
804719
guard of `if x < 5`.
805720

@@ -813,7 +728,7 @@ match num {
813728
}
814729
```
815730

816-
<span class="caption">Listing 18-29: Adding a match guard to a pattern</span>
731+
<span class="caption">Listing 18-26: Adding a match guard to a pattern</span>
817732

818733
This example will print `less than five: 4`. When `num` is compared to the
819734
pattern in the first arm, it matches, because `Some(4)` matches `Some(x)`. Then
@@ -852,7 +767,7 @@ fn main() {
852767
}
853768
```
854769

855-
<span class="caption">Listing 18-30: Using a match guard to test for equality
770+
<span class="caption">Listing 18-27: Using a match guard to test for equality
856771
with an outer variable</span>
857772

858773
This code will now print `Default case, x = Some(5)`. The pattern in the second
@@ -884,7 +799,7 @@ match x {
884799
}
885800
```
886801

887-
<span class="caption">Listing 18-31: Combining multiple patterns with a match
802+
<span class="caption">Listing 18-18: Combining multiple patterns with a match
888803
guard</span>
889804

890805
The match condition states that the arm only matches if the value of `x` is
@@ -941,7 +856,7 @@ match msg {
941856
}
942857
```
943858

944-
<span class="caption">Listing 18-32: Using `@` to bind to a value in a pattern
859+
<span class="caption">Listing 18-19: Using `@` to bind to a value in a pattern
945860
while also testing it</span>
946861

947862
This example will print `Found an id in range: 5`. By specifying `id_variable
@@ -963,6 +878,66 @@ first two arms: any value would match this pattern.
963878

964879
Using `@` lets us test a value and save it in a variable within one pattern.
965880

881+
### Legacy patterns: `ref` and `ref mut`
882+
883+
In older versions of Rust, `match` would assume that you want to move what is
884+
matched. But sometimes, that's not what you wanted. For example:
885+
886+
```rust
887+
let robot_name = &Some(String::from("Bors"));
888+
889+
match robot_name {
890+
Some(name) => println!("Found a name: {}", name),
891+
None => (),
892+
}
893+
894+
println!("robot_name is: {:?}", robot_name);
895+
```
896+
897+
Here, `robot_name` is a `&Option<String>`. Rust would then complain that
898+
`Some(name)` doesn't match up with `&Option<T>`, so you'd have to write this:
899+
900+
```rust
901+
let robot_name = &Some(String::from("Bors"));
902+
903+
match robot_name {
904+
&Some(name) => println!("Found a name: {}", name),
905+
None => (),
906+
}
907+
908+
println!("robot_name is: {:?}", robot_name);
909+
```
910+
911+
Next, Rust would complain that `name` is trying to move the `String` out of
912+
the option, but because it's a reference to an option, it's borrowed, and so
913+
can't be moved out of. This is where the `ref` keyword comes into play:
914+
915+
```rust
916+
let robot_name = &Some(String::from("Bors"));
917+
918+
match robot_name {
919+
&Some(ref name) => println!("Found a name: {}", name),
920+
None => (),
921+
}
922+
923+
println!("robot_name is: {:?}", robot_name);
924+
```
925+
926+
The `ref` keyword is like the opposite of `&` in patterns; this says "please
927+
bind `ref` to be a `&String`, don't try to move it out. In other words, the
928+
`&` in `&Some` is matching against a reference, but `ref` *creates* a
929+
reference. `ref mut` is like `ref`, but for mutable references.
930+
931+
Anyway, today's Rust doesn't work like this. If you try to `match` on
932+
something borrowed, then all of the bindings you create will attempt to
933+
borrow as well. This means that the original code works as you'd expect.
934+
935+
Because Rust is backwards compatible, we couldn't remove `ref` and `ref mut`,
936+
and they're sometimes useful in obscure situations, where you want to
937+
partially borrow part of a struct as mutable and another part as immutable.
938+
But you may see them in older Rust code, so knowing what they do is still
939+
useful.
940+
966941
## Summary
967942

968943
Rust’s patterns are very useful in that they help distinguish between different

0 commit comments

Comments
 (0)