十六、Rust patterns
patterns是 Rust 中特有的语法,用于匹配类型中的结构patterns组成:- 字面量 (字面值)
- 解构的数组、枚举、结构体、元组
- 变量
- 通配符
- 占位符
1. 使用位置
-
match分支 -
if..let表达式 -
while..let条件循环 -
for..in循环// 来自官方教程的一个示例 let v = vec![1, 2, 3]; for (index, value) in v.iter().enumerate() { println!("{} is at index {}", value, index); } -
let语句// 官方解释:let 语句更为正式的样子如下 let PATTERN = EXPRESSION; // 变量模式 // 元组解构模式,可以视为多个 `变量模式` 的组合 let (x, y, z) = (1, 2, 3); -
函数参数
- 在函数中的参数签名,也是一种模式
- 类似的,在闭包中的签名,也是一种模式
2. 可反驳性
- 可反驳性:Refutability
- 模式有两种形式:refutable(可反驳的)和 irrefutable(不可反驳的)
- 不可反驳的
- 能匹配任何传递的可能值的模式
- 比如:函数参数、
let语句和for循环- 这三个只能接受不可反驳的模式
- 可反驳的
- 对某些值进行匹配时有可能匹配失败的模式
- 比如:
if..let,match,while..let等条件语句 - 只允许接受可反驳的模式:
if let和while let表达式
- 特殊的模式:
match分支- 匹配分支必须使用可反驳模式
- 最后一个分支必须使用能匹配任何剩余值的不可反驳模式
- 可以在只有一个匹配分支的
match中使用不可反驳模式,但这没有意义
3. 模式语法
(注:以下的代码均来自官方教程)
3.1 匹配字面值
- 字面值,在
Javascript中叫字面量
let x = 1;
match x {
1 => println!("one"),
2 => println!("two"),
3 => println!("three"),
_ => println!("anything"),
}
3.2 匹配命名变量
-
命名变量是匹配任何值的不可反驳模式
fn main() { let x = Some(5); let y = 10; match x { Some(50) => println!("Got 50"), // match 会创建一个新的作用域,因此这里的 y 和外面的 y 是不同的 // Some(var) 中的 var 会匹配任何 Some 中的值,并将这个值赋值给自己 Some(y) => println!("Matched, y = {:?}", y), _ => println!("Default case, x = {:?}", x), } println!("at the end: x = {:?}, y = {:?}", x, y); }
3.3 匹配多个模式
-
在
match表达式中,可以使用|语法匹配多个模式let x = 1; match x { 1 | 2 => println!("one or two"), 3 => println!("three"), _ => println!("anything"), }
3.4 匹配值的范围
-
..=语法允许你匹配一个闭区间范围内的值- 只允许用于数字或
char值
let x = 5; match x { 1..=5 => println!("one through five"), _ => println!("something else"), } - 只允许用于数字或
3.5 解构
-
解构元组
fn main() { let (x, y, z) = (1, 2, 3); println!("x = {}, y = {}, z = {}", x, y, z); } -
解构结构体
struct Point { x: i32, y: i32, } fn main() { let p = Point { x: 0, y: 7 }; let Point { x, y: b } = p; assert_eq!(0, x); assert_eq!(7, b); } -
解构枚举
enum Message { Quit, Write(String), Move { x: i32, y: i32 }, ChangeColor(i32, i32, i32), } fn main() { let msg = Message::ChangeColor(0, 160, 255); match msg { Message::Quit => { println!("The Quit variant has no data to destructure.") } Message::Write(text) => { println!("Text message: {}", text); } Message::Move { x, y } => { println!("x direction {}, y direction {}", x, y); } Message::ChangeColor(r, g, b) => { println!("red {}, green {}, blue {}", r, g, b); } } } -
嵌套的解构
enum Color { Rgb(i32, i32, i32), Hsv(i32, i32, i32), } enum Message { Quit, Write(String), Move { x: i32, y: i32 }, ChangeColor(Color), } fn main() { let msg = Message::ChangeColor(Color::Hsv(0, 160, 255)); match msg { Message::ChangeColor(Color::Rgb(r, g, b)) => { println!("red {}, green {}, blue {}", r, g, b) } Message::ChangeColor(Color::Hsv(h, s, v)) => { println!("hue {}, saturation {}, value {}", h, s, v) } _ => () } } -
一个复杂的解构
let ((feet, inches), Point {x, y}) = ((3, 10), Point { x: 3, y: -10 });
3.6 忽略模式值
-
使用
_忽略整个值fn main() { let x = 5; match x { 0 => { println!("x is zero"); } _ => { println!("x is not zero"); } } foo(1, 2); } fn foo(_: i32, b: i32) { println!("The second param, b, is {}", b); } -
使用
_忽略部分值let mut setting_value = Some(5); let new_setting_value = Some(10); match (setting_value, new_setting_value) { // 当不需要 Some 中的值时,在模式内使用下划线来匹配 Some 成员 (Some(_), Some(_)) => { println!("Can't overwrite an existing customized value"); } _ => { setting_value = new_setting_value; } } println!("setting is {:?}", setting_value); -
忽略未使用的变量,包括函数参数
-
在名字前以一个下划线开头
fn main() { let _x = 5; let y = 10; foo(10, 20); } fn foo(_a: i32, b: i32) { // 参数列表 (_a: i32, b: i32) 和 (_: i32, b: i32) 的区别: // 第一个参数列表,会将传入的值绑定到 _a 上; // 第二个参数列表,不会发生值绑定 println!("param b is {}.", b); }
-
-
用
..忽略剩余值-
结构体、元组都可以使用。但是在元组中,一次结构时只能用一次这个特性
struct Point { x: i32, y: i32, z: i32, } fn main() { // 解构元组 let tuple = (1, 2, 3); let (_first, ..) = tuple; // let (.., second, ..) = tuple; // 不允许 let (.., _third) = tuple; let (_1st, .., _3rd) = tuple; // 解构结构体 let origin = Point { x: 0, y: 0, z: 0 }; match origin { Point { x, .. } => println!("x is {}", x), } }
-
3.7 匹配守卫 提供额外条件
-
匹配守卫:match guard
- 指定于
match分支模式之后的额外if条件,只有这个if条件被满足时,才能选择这个分支
- 指定于
-
示例
let num = Some(4); match num { Some(x) if x < 5 => println!("less than five: {}", x), Some(x) => println!("{}", x), None => (), } -
解决变量覆盖的问题
fn main() { let x = Some(5); let y = 10; match x { Some(50) => println!("Got 50"), Some(n) if n == y => println!("Matched, n = {}", n), _ => println!("Default case, x = {:?}", x), } println!("at the end: x = {:?}, y = {}", x, y); } -
或运算符与匹配守卫
fn main(){ let x = 4; let y = false; match x { // 或运算符 `|` 优先级高于匹配守卫 4 | 5 | 6 if y => println!("yes"), _ => println!("no"), } }
3.8 @ 绑定
-
允许我们在创建一个存放值的变量的同时,测试其值是否匹配模式
enum Message { Hello { id: i32 }, } fn main() { let msg = Message::Hello { id: 5 }; match msg { // 判断 id 是否在 3~7 这个范围,并将 id 重命名为 id_variable Message::Hello { id: id_variable @ 3..=7 } => { println!("Found an id in range: {}", id_variable) }, // 判断 id 是否在 10~12 这个范围 Message::Hello { id: 10..=12 } => { println!("Found an id in another range") }, Message::Hello { id } => { println!("Found some other id: {}", id) }, } }