-
Notifications
You must be signed in to change notification settings - Fork 13.3k
Guide: match #15434
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Guide: match #15434
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -956,6 +956,102 @@ Enums | |
|
||
## Match | ||
|
||
Often, a simple `if`/`else` isn't enough, because you have more than two | ||
possible options. And `else` conditions can get incredibly complicated. So | ||
what's the solution? | ||
|
||
Rust has a keyword, `match`, that allows you to replace complicated `if`/`else` | ||
groupings with something more powerful. Check it out: | ||
|
||
```rust | ||
let x = 5i; | ||
|
||
match x { | ||
1 => println!("one"), | ||
2 => println!("two"), | ||
3 => println!("three"), | ||
4 => println!("four"), | ||
5 => println!("five"), | ||
_ => println!("something else"), | ||
} | ||
``` | ||
|
||
`match` takes an expression, and then branches based on its value. Each 'arm' of | ||
the branch is of the form `val => expression`. When the value matches, that arm's | ||
expression will be evaluated. It's called `match` because of the term 'pattern | ||
matching,' which `match` is an implementation of. | ||
|
||
So what's the big advantage here? Well, there are a few. First of all, `match` | ||
does 'exhaustiveness checking.' Do you see that last arm, the one with the | ||
underscore (`_`)? If we remove that arm, Rust will give us an error: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It would be nice to write the failing code here, so the readers can try it out and experience the error for themselves. |
||
|
||
```{ignore,notrust} | ||
error: non-exhaustive patterns: `_` not covered | ||
``` | ||
|
||
In other words, Rust is trying to tell us we forgot a value. Because `x` is an | ||
integer, Rust knows that it can have a number of different values. For example, | ||
`6i`. But without the `_`, there is no arm that could match, and so Rust refuses | ||
to compile. `_` is sort of like a catch-all arm. If none of the other arms match, | ||
the arm with `_` will. And since we have this catch-all arm, we now have an arm | ||
for every possible value of `x`, and so our program will now compile. | ||
|
||
`match` statements also destructure enums, as well. Remember this code from the | ||
section on enums? | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It would be nice to have a hyperlink here. My personal guess is that most users will not read this guide from one end to the other in one go. (But time will tell) |
||
|
||
```{rust} | ||
let x = 5i; | ||
let y = 10i; | ||
|
||
let ordering = x.cmp(&y); | ||
|
||
if ordering == Less { | ||
println!("less"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Perhaps this line could be |
||
} else if ordering == Greater { | ||
println!("greater"); | ||
} else if ordering == Equal { | ||
println!("equal"); | ||
} | ||
``` | ||
|
||
We can re-write this as a `match`: | ||
|
||
```{rust} | ||
let x = 5i; | ||
let y = 10i; | ||
|
||
match x.cmp(&y) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just an idea: I think that this example could be simpler if you used an enum of directions instead . enum Directions {East, South, West North}
let direction = East;
match direction {
East => println!("The direction is East"),
South => println!("The direction is South"),
West => println!("The direction is West"),
North => println!("The direction is North"),
} Then the reader would not have to worry about the meaning of the method named cmp and why you have to use a reference to enum Directions {East, South, West North}
let direction = East;
let name = match direction {
East => "East",
South => "South",
West => "West",
North => "North",
}
println!("The direction is {}", name), There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We will be using |
||
Less => println!("less"), | ||
Greater => println!("greater"), | ||
Equal => println!("equal"), | ||
} | ||
``` | ||
|
||
This version has way less noise, and it also checks exhaustively to make sure | ||
that we have covered all possible variants of `Ordering`. With our `if`/`else` | ||
version, if we had forgotten the `Greater` case, for example, our program would | ||
have happily compiled. If we forget in the `match`, it will not. Rust helps us | ||
make sure to cover all of our bases. | ||
|
||
`match` is also an expression, which means we can use it on the right hand side | ||
of a `let` binding. We could also implement the previous line like this: | ||
|
||
``` | ||
let x = 5i; | ||
let y = 10i; | ||
|
||
let result = match x.cmp(&y) { | ||
Less => "less", | ||
Greater => "greater", | ||
Equal => "equal", | ||
}; | ||
|
||
println!("{}", result); | ||
``` | ||
|
||
In this case, it doesn't make a lot of sense, as we are just making a temporary | ||
string where we don't need to, but sometimes, it's a nice pattern. | ||
|
||
## Looping | ||
|
||
for | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps remove the options 4 and 5 here (Just an idea)