文章题目来自:https://practice-zh.course.rs/pattern-match/patterns.html
1 🌟🌟 使用 | 可以匹配多个值, 而使用 ...= 可以匹配一个闭区间的数值序列
rust
fn main() {}
fn match_number(n: i32) {
match n {
// 匹配一个单独的值
1 => println!("One!"),
// 使用 `|` 填空,不要使用 `..` 或 `..=`
__ => println!("match 2 -> 5"),
// 匹配一个闭区间的数值序列
6..=10 => {
println!("match 6 -> 10")
},
_ => {
println!("match 11 -> +infinite")
}
}
}
将前面学到的模式匹配进行集合就可以了
rust
fn match_number(n: i32) {
match n {
// 匹配一个单独的值
1 => println!("One!"),
// 使用 `|` 填空,不要使用 `..` 或 `..=`
2 | 3 | 4 | 5 => println!("match 2 -> 5"),
// 匹配一个闭区间的数值序列
6..=10 => {
println!("match 6 -> 10")
}
others => {
println!("match 11 -> +infinite")
}
}
}
2 🌟🌟🌟 @ 操作符可以让我们将一个与模式相匹配的值绑定到新的变量上
rust
struct Point {
x: i32,
y: i32,
}
fn main() {
// 填空,让 p 匹配第二个分支
let p = Point { x: __, y: __ };
match p {
Point { x, y: 0 } => println!("On the x axis at {}", x),
// 第二个分支
Point { x: 0..=5, y: y@ (10 | 20 | 30) } => println!("On the y axis at {}", y),
Point { x, y } => println!("On neither axis: ({}, {})", x, y),
}
}
这里的语法可能会让有些人非常疑惑:明明x
和y
就在这里 ,为什么我要再给y
绑定一个变量?如果你直接编译上述代码(我指的是修改了p
的赋值之后),你会遇到如下错误:
将y
改成p.y
就可以了。
我们先考虑另外一个场景:
rust
fn age() -> u32 {
15
}
fn main() {
println!("Tell me what type of person you are");
match age() {
0 => println!("I haven't celebrated my first birthday yet"),
1..=12 => println!("I'm a child of age 1..12"),
13..=19 => println!("I'm a teen of age 13..19"),
_ => println!("I'm an old person of age others"),
}
}
一个典型的根据年龄匹配打印的函数,那如果我希望将被匹配的值,也就是age()
的返回值打印出来呢?这就不太好办了。也许可以在println!
里再调用一次?你知道可以这么做只是因为这里age()
返回的值是固定的,如果它每次返回的值都不一样,显然不能这么做。
这时,@
绑定就派上用场了。
rust
fn main() {
println!("Tell me what type of person you are");
match age() {
0 => println!("I haven't celebrated my first birthday yet"),
n @ 1..=12 => println!("I'm a child of age {:?}", n),
n @ 13..=19 => println!("I'm a teen of age {:?}", n),
n => println!("I'm an old person of age {:?}", n),
}
}
我们将被匹配的值绑定到n
上,后续代码逻辑就可以使用了。
再考虑上面的例子,归根结底,每次匹配都只是将外部的变量和范围进行匹配,中间没有产生任何额外的变量 。所以如果你想使用,要么使用原始的外部变量,要么进行一个@
绑定。而匹配所有值,本质上不是匹配,就是进行了一次绑定,所以绑定的值可以直接使用。
3
rust
// 修复错误
enum Message {
Hello { id: i32 },
}
fn main() {
let msg = Message::Hello { id: 5 };
match msg {
Message::Hello {
id: 3..=7,
} => println!("id 值的范围在 [3, 7] 之间: {}", id),
Message::Hello { id: newid@10 | 11 | 12 } => {
println!("id 值的范围在 [10, 12] 之间: {}", newid)
}
Message::Hello { id } => println!("Found some other id: {}", id),
}
}
第一个需要用@
绑定,第二个需要全部匹配(落个括号)
rust
// 修复错误
enum Message {
Hello { id: i32 },
}
fn main() {
let msg = Message::Hello { id: 5 };
match msg {
Message::Hello {
id:id@ 3..=7,
} => println!("id 值的范围在 [3, 7] 之间: {}", id),
Message::Hello { id: newid@(10 | 11 | 12) } => {
println!("id 值的范围在 [10, 12] 之间: {}", newid)
}
Message::Hello { id } => println!("Found some other id: {}", id),
}
}
4 🌟🌟 匹配守卫(match guard)是一个位于 match 分支模式之后的额外 if 条件,它能为分支模式提供更进一步的匹配条件。
rust
// 填空让代码工作,必须使用 `split`
fn main() {
let num = Some(4);
let split = 5;
match num {
Some(x) __ => assert!(x < split),
Some(x) => assert!(x >= split),
None => (),
}
}
事实上就是多一个if条件,来进一步匹配而已。
rust
fn main() {
let num = Some(4);
let split = 5;
match num {
Some(x) if x < split => assert!(x < split),
Some(x) => assert!(x >= split),
None => (),
}
}
5 🌟🌟🌟 使用 ... 忽略一部分值
rust
// 填空,让代码工作
fn main() {
let numbers = (2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048);
match numbers {
__ => {
assert_eq!(first, 2);
assert_eq!(last, 2048);
}
}
}
可惜只能忽略一次
rust
fn main() {
let numbers = (2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048);
match numbers {
(first, .., last) => {
assert_eq!(first, 2);
assert_eq!(last, 2048);
}
}
}
6 🌟🌟 使用模式 &mut V 去匹配一个可变引用时,你需要格外小心,因为匹配出来的 V 是一个值,而不是可变引用
rust
// 修复错误,尽量少地修改代码
// 不要移除任何代码行
fn main() {
let mut v = String::from("hello,");
let r = &mut v;
match r {
&mut value => value.push_str(" world!")
}
}
思来想去,也只有这一种改法了。
rust
fn main() {
let mut v = String::from("hello,");
let r = &mut v;
match r {
value => value.push_str(" world!")
}
}
r
本身就是对string
的可变引用,如果想直接使用的话,没必要再引用一次。按图中的匹配,最后value
会是一个不可变string
,这样后面的操作就做不到了。