mirror of
https://github.com/rust-lang-cn/book-cn.git
synced 2025-02-02 23:38:41 +08:00
second half
This commit is contained in:
parent
f23e4b3d5a
commit
1227640bc0
66
src/if-let.md
Normal file
66
src/if-let.md
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
# if let
|
||||||
|
|
||||||
|
There's one more advanced control flow structure we haven't discussed: `if
|
||||||
|
let`. Imagine we're in a situation like this:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
# let some_option = Some(5);
|
||||||
|
match some_option {
|
||||||
|
Some(x) => {
|
||||||
|
// do something with x
|
||||||
|
},
|
||||||
|
None => {},
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
We care about the `Some` case, but don't want to do anything with the `None`
|
||||||
|
case. With an `Option`, this isn't _too_ bad, but with a more complex enum,
|
||||||
|
adding `_ => {}` after processing just one variant doesn't feel great. We have
|
||||||
|
this boilerplate arm, and we have an extra level of indentation: the code that
|
||||||
|
does something with `x` is indented twice, rather than just once. We really want
|
||||||
|
a construct that says "Do something with this one case, I don't care about the
|
||||||
|
others."
|
||||||
|
|
||||||
|
Enter `if let`:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
# let some_option = Some(5);
|
||||||
|
if let Some(x) = some_option {
|
||||||
|
// do something with x
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`if let` takes a pattern and an expression, separated by an `=`. It works
|
||||||
|
exactly like a `match`, where the expression is given to the `match`, and the
|
||||||
|
pattern is its first arm. In other words, you can think of `if let` as syntax
|
||||||
|
sugar:
|
||||||
|
|
||||||
|
```rust,ignore
|
||||||
|
if let pattern = expression {
|
||||||
|
body
|
||||||
|
}
|
||||||
|
|
||||||
|
match expression {
|
||||||
|
pattern => body,
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
And in fact, we can include an `else`, too, and it becomes the body of the `_`
|
||||||
|
case:
|
||||||
|
|
||||||
|
```rust,ignore
|
||||||
|
if let pattern = expression {
|
||||||
|
body
|
||||||
|
} else {
|
||||||
|
else_body
|
||||||
|
}
|
||||||
|
|
||||||
|
match expression {
|
||||||
|
pattern => body,
|
||||||
|
_ => else_body,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
In other words, it's the high-level construct we were originally looking for:
|
||||||
|
do something with a single pattern.
|
238
src/patterns.md
Normal file
238
src/patterns.md
Normal file
@ -0,0 +1,238 @@
|
|||||||
|
# Patterns
|
||||||
|
|
||||||
|
We've mentioned 'patterns' a few times so far: they're used in `let` bindings,
|
||||||
|
in function arguments, and in the `match` expression. Patterns have a lot of
|
||||||
|
abilities, so in this section, we'll cover all of the different things they can
|
||||||
|
do. Any of these abilities work in any place where a pattern is used.
|
||||||
|
|
||||||
|
## Literals & _
|
||||||
|
|
||||||
|
You can match against literals directly, and `_` acts as an any case:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let x = 1;
|
||||||
|
|
||||||
|
match x {
|
||||||
|
1 => println!("one"),
|
||||||
|
2 => println!("two"),
|
||||||
|
3 => println!("three"),
|
||||||
|
_ => println!("anything"),
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This prints `one`.
|
||||||
|
|
||||||
|
# Multiple patterns
|
||||||
|
|
||||||
|
You can match multiple patterns with `|`:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let x = 1;
|
||||||
|
|
||||||
|
match x {
|
||||||
|
1 | 2 => println!("one or two"),
|
||||||
|
3 => println!("three"),
|
||||||
|
_ => println!("anything"),
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This prints `one or two`.
|
||||||
|
|
||||||
|
## ref and ref mut
|
||||||
|
|
||||||
|
Usually, when you match against a pattern, bindings: are bound by value.
|
||||||
|
This means you'll end up moving the value out:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let name = Some(String::from("Bors"));
|
||||||
|
|
||||||
|
match name {
|
||||||
|
Some(name) => println!("Found a name: {}", name),
|
||||||
|
None => (),
|
||||||
|
}
|
||||||
|
|
||||||
|
// name is moved here
|
||||||
|
```
|
||||||
|
|
||||||
|
If you'd prefer to bind `name` by reference, use the `ref` keyword:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let name = Some(String::from("Bors"));
|
||||||
|
|
||||||
|
match name {
|
||||||
|
Some(ref name) => println!("Found a name: {}", name),
|
||||||
|
None => (),
|
||||||
|
}
|
||||||
|
|
||||||
|
// name is not moved here; the match only took a reference to its data rather
|
||||||
|
// than moving it
|
||||||
|
```
|
||||||
|
|
||||||
|
And for a mutable reference, `ref mut`:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let mut name = Some(String::from("Bors"));
|
||||||
|
|
||||||
|
match name {
|
||||||
|
Some(ref mut name) => *name = String::from("Another name"),
|
||||||
|
None => (),
|
||||||
|
}
|
||||||
|
|
||||||
|
// name is not moved here; the match only took a reference to its data rather
|
||||||
|
// than moving it
|
||||||
|
```
|
||||||
|
|
||||||
|
## Destructuring
|
||||||
|
|
||||||
|
Patterns can be used to destructure structs and enums:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
struct Point {
|
||||||
|
x: i32,
|
||||||
|
y: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
let origin = Point { x: 0, y: 0 };
|
||||||
|
|
||||||
|
let Point { x, y } = origin;
|
||||||
|
```
|
||||||
|
|
||||||
|
This brings an `x` and `y` binding 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`:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
struct Point {
|
||||||
|
x: i32,
|
||||||
|
y: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
let origin = Point { x: 0, y: 0 };
|
||||||
|
|
||||||
|
match origin {
|
||||||
|
Point { x, y } => { }, // x and y are bound here
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Shadowing
|
||||||
|
|
||||||
|
As with all bindings, anything bound by a pattern will shadow bindings
|
||||||
|
outside of the binding construct:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let x = Some(5);
|
||||||
|
|
||||||
|
match x {
|
||||||
|
Some(x) => { }, // x is an i32 here, not an Option<i32>
|
||||||
|
None => (),
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Ignoring bindings
|
||||||
|
|
||||||
|
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:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let x = Some(5);
|
||||||
|
|
||||||
|
match x {
|
||||||
|
Some(_) => println!("got a Some and I don't care what's inside"),
|
||||||
|
None => (),
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Or like this:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let numbers = (2, 4, 8, 16, 32);
|
||||||
|
|
||||||
|
match numbers {
|
||||||
|
(first, _, third, _, fifth) => println!("Some numbers: {}, {}, {}", first, third, fifth),
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
If you want, you can use `..` to ignore all of the parts you haven't defined:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
struct Point {
|
||||||
|
x: i32,
|
||||||
|
y: i32,
|
||||||
|
z: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
let origin = Point { x: 0, y: 0, z: 0 };
|
||||||
|
|
||||||
|
match origin {
|
||||||
|
Point { x, .. } => { }, // y and z are ignored
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Ranges
|
||||||
|
|
||||||
|
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 usually used with integers or `char`s:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
fn main() {
|
||||||
|
let x = 'c';
|
||||||
|
|
||||||
|
match x {
|
||||||
|
'a' ... 'j' => println!("early ASCII letter"),
|
||||||
|
'k' ... 'z' => println!("late ASCII letter"),
|
||||||
|
_ => println!("something else"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Guards
|
||||||
|
|
||||||
|
You can introduce match guards with `if`:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let x = Some(5);
|
||||||
|
|
||||||
|
match x {
|
||||||
|
Some(x) if x < 5 => println!("less than five: {}", x),
|
||||||
|
None => (),
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
If youre using if with multiple patterns, the if applies to both sides:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let x = 4;
|
||||||
|
let y = false;
|
||||||
|
|
||||||
|
match x {
|
||||||
|
4 | 5 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:
|
||||||
|
|
||||||
|
```text
|
||||||
|
(4 | 5) if y => ...
|
||||||
|
```
|
||||||
|
|
||||||
|
not this:
|
||||||
|
|
||||||
|
```text
|
||||||
|
4 | (5 if y) => ...
|
||||||
|
```
|
||||||
|
|
||||||
|
## Bindings
|
||||||
|
|
||||||
|
You can bind values to names with `@`:
|
Loading…
Reference in New Issue
Block a user