👈 15 面向对象
所有可能会用到模式的位置
match
匹配一个 Option<i32>
,如果为 Some(n)
,返回 Some(n + 1)
,否则返回 None
:
let x = Some(2);
match x {
None => None,
Some(n) => Some(n + 1),
}
编译器要求 match
进行穷尽匹配,具名变量可以匹配任何模式,匿名变量则丢弃任何模式。
if let
一个复杂的 if let
语句,包含很多可选子句:
fn main() {
let favorite_color: Option<&str> = None;
let is_tuesday = false;
let age: Result<u8, _> = "34".parse();
if let Some(color) = favorite_color {
println!("{color}");
} else if is_tuesday { // 可以有 else if
println!("green");
} else if let Ok(age) = age { // 还可以有 else if let
if age > 30 { // 不能归入 else if let, 因为 age 只在 {} 内有效
println!("purple");
} else {
println!("orange");
}
} else { // 最后, 可以有 else
println!("blue");
}
}
编译器不会检查 if let
的穷尽性,适用于不要求穷尽的情况。
while let
类似于 if let
,但只要模式匹配就一直执行 while
。
在栈非空时持续弹出并打印元素:
let mut stk = Vec::new();
stk.push(1);
stk.push(2);
stk.push(3);
while let Some(top) = stk.pop() {
println!("top={top}");
}
for
解构元组并用于 for
:
let arr = vec!['a', 'b', 'c'];
for (i, x) in arr.iter().enumerate() {
println!("arr[{}]={}", i, x);
}
let
这也是模式匹配:
let x = 0;
解构元组:
let (x, y) = (3, 4);
解构元组,用匿名变量 _
或 ..
忽略部分元素:
let (a, b, _) = (6, 2, 0);
函数参数
在函数参数中解构元组:
fn main() {
let v = (3.0, 4.0);
assert!((len(&v) - 5.0).abs() <= 0.00001);
}
fn len(&(x, y): &(f64, f64)) -> f64 {
(x * x + y * y).sqrt()
}
可反驳性:模式是否会匹配失效
模式有两种形式:可反驳的(refutable) 和 不可反驳的(irrefutable),称能匹配任何传递的可能值的模式为不可反驳的。
for
/ let
/ 函数参数只能接受不可反驳的模式(否则,如果模式不匹配,程序将无法执行),而 if let
/ while let
对两种形式都能接受,但是通常要接受可反驳的模式才有意义(如果不可反驳,if
就不会失败,while
也不会终止,编译器将发出警告)。
尝试在 let
中使用可反驳的模式:
let Some(x) = Some(1);
println!("x={x}");
视觉上,似乎应该打印 x = 1
,但是无法过编:
error[E0005]: refutable pattern in local binding
--> src\main.rs:2:9
|
2 | let Some(x) = Some(1);
| ^^^^^^^ pattern `None` not covered
|
= note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
= note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
= note: the matched value is of type `Option<i32>`
help: you might want to use `let else` to handle the variant that isn't matched
|
2 | let Some(x) = Some(1) else { todo!() };
| ++++++++++++++++
应该使用 if let
:
if let Some(x) = Some(1) {
println!("x={x}");
}
所有的模式语法
match
match x {
1 => {} // 字面值
2 | 3 => {} // 多个模式
1..=10 => {} // 范围值
Some(n) => {} // 命名变量(枚举)
Point { x: 0, y } => {} // 命名变量(结构体) // 匹配 y 轴上的点
_ => {} // 匿名变量
}
解构
解构结构体:
struct Point {
x: i32,
y: i32,
}
fn main() {
let p = Point { x: 0, y: 7 };
let Point { x, y } = p; // 等效于元组解构 let (x, y) = (0, 7)
assert_eq!(0, x);
assert_eq!(7, y);
}
解构枚举:
enum Message {
Quit,
Write(String),
Move { x: i32, y: i32 },
}
fn main() {
let msg = Message::Move { x: 1, y: 2 };
match msg {
Message::Quit => {}
Message::Write(text) => {}
Message::Move { x, y } => {}
}
}
忽略模式中的值
用 _
忽略全部值
在函数签名中用 _
忽略某个参数:
fn main() {
foo(3, 4);
}
fn foo(_: i32, y: i32) {
println!("Only y matters: y={y}");
}
在变量名前用 _
忽略未使用变量:
fn main() {
let _x = 5; // no warning
let y = 10; // warning
}
用 ..
忽略剩余值
如果有较多值需要用 _
忽略,可以改用 ..
:
struct Point {
x: i32,
y: i32,
z: i32,
}
fn main() {
let p = Point { x: 0, y: 0, z: 0 };
match p {
Point { x, .. } => println!("x={x}"),
}
}
在不产生歧义的前提下,..
将自动扩展为所需要的值的数量:
fn main() {
let arr = (2, 4, 8, 16, 32);
match arr {
(first, .., last) => {
println!("first={first}, last={last}");
}
}
}
匹配守卫提供的额外条件
匹配守卫(match guard) 是 match
的模式分支内额外的 if
,进门(匹配 match
分支)后,还要通过守卫的检查(满足 if
),才能执行分支:
let num = Some(4);
match num {
Some(x) if x % 2 == 0 => println!("{x} is even"),
Some(x) => println!("{x} is odd"),
None => (),
}
也可以同时使用 |
指定多个模式:
let x = 4;
let y = false;
match x {
4 | 5 | 6 if y => println!("yes"),
_ => println!("no"),
}
@
绑定
@
:创建一个存放值的变量的同时测试其值是否匹配模式。
enum Message {
Hello { id: i32 },
}
fn main() {
let msg = Message::Hello { id: 5 };
match msg {
Message::Hello {
id: id_variable @ 3..=7,
} => println!("Id in [3,7]: {id_variable}"),
Message::Hello { id: 10..=12 } => {
println!("Id is in [10,12]")
}
Message::Hello { id } => println!("Invalid Id: {id}"),
}
}
id_variable
的命名是为了演示用,其实它可以与字段 id
同名。
👉 17 高级特征