高级匹配
match 关键字我们已经在Option Result 以及 枚举类型中见到过它的基本使用方法,类似于C里面的switch case,
但是match 在RUST中还有更加高级的用法
模式
要理解match 首先需要理解模式
模式:模式基于 给定数据结构去匹配值 并 解构,并可选地将变量和这些结构中匹配到的值绑定起来。 模式也用在变量声明上和函数(包括闭包)的参数上
下面示例中的模式完成四件事:
- 测试 person 是否在其 car字段中填充了内容。
- 测试 person 的 age字段(的值)是否在 13 到 19 之间,并将其值绑定到给定的变量 person_age 上。
- 将对 name字段的引用绑定到给定变量 person_name 上。
- 忽略 person 的其余字段。其余字段可以有任何值,并且不会绑定到任何变量上。
if let
Person {
car: Some(_),
age: person_age @ 13..=19,
name: ref person_name,
..
} = person
{
println!("{} has a car and is {} years old.", person_name, person_age);
}
模式可以被使用在
- let 声明
- 函数和闭包的参数
- 匹配(match)表达式
- if let表达式
- while let表达式
- for表达式
模式的解构
模式可以解构 结构体(struct)、枚举(enum)和元组,
占位符(_) 代表一个数据字段,而通配符 .. 代表特定变量/变体(variant)的所有剩余字段
解构的应用,这部分我们在讲解结构体和元组已经讲解过了
enum Color {
RED(i32),
BLUE(i32),
}
fn main() {
let a = (10,20,30,40,50);
let (x,y,_,..) = a; // let 可以使用模式 对元组解构 _忽略第三个数据,..忽略剩余的其他字段
println!("{} {} ",x,y);
let red = Color::RED(100);
if let Color::RED(x) = red { // if let 对enum 解构
println!("{}",x);
}
if let (x,y,_,..) = a { // if let 对元组解构
println!("{} {} ",x,y);
}
}
解构是模式的基本使用方法,能实现功能比较单一 一般都要继续结合值匹配使用
模式的匹配
可反驳性:当一个模式有可能和它匹配的值不相同时,我们说这个模式具备 可反驳性
不可反驳性:当一个模式总是和它匹配的值相同时,我们说这个模式具备 不可反驳性
在我们前一个小节的例子中中,请说明哪个是可反驳 哪个是不可反驳的
let (x, y) = (1, 2); // "(x, y)" 是一个不可反驳型模式
if let (a, 3) = (1, 2) { // "(a, 3)" 是可反驳型的, 将不会匹配
panic!("Shouldn't reach here");
} else if let (a, 4) = (3, 4) { // "(a, 4)" 是可反驳型的, 将会匹配
println!("Matched ({}, 4)", a);
}
没有条件的解构,一般都是不可反驳的,因为能匹配一切,带有条件的模式匹配,一般都是具备不可反驳性
接下来 我们讲从最简单的匹配开始讲起
字面量模式
字面量是最简单的匹配, 支持: 数字、bool、char、string
@ 表示变量绑定,如果匹配成功,匹配到的值会绑定在 变量上
_ 是通配符匹配,表示可以匹配任何值
fn main() {
let x = 10_i32;
if let y @ 10 = x {
println!("y is 10 : {}",y);
}
if let y @ 20 = x {
println!("y is 20 : {}",y);
}
if let _ @ 10 = x {
println!("y is 10");
}
let y = "abc";
match y {
"123" => println!("this is 123"),
z @ "abc" => println!("this is abc {}",z),
_ => println!("this is other "),
}
}
标识符模式
标识符模式将它们匹配的值绑定到一个变量上。此标识符在该模式中必须是唯一的。 该变量会在作用域中遮蔽任何同名的变量。这种绑定的作用域取决于使用模式的上下文。
在上面我们已经见到了,标识符模式主要用于变量的绑定,@ mut ref 都是标识符绑定
fn main() {
let mut a = 10; // mut 是一个标识符模式,标识绑定变量a的时候,使用可变模式
let ref a_ref = a; // ref 是一个标识符模式,标识绑定变量 a_ref 的时候,使用的是a的引用 等于 let a_ref= &a;
if let y @ 10 = a { // @是一个标识符模式,标识把匹配到的值 绑定在 一个变量上
println!("a is 10 : {}",y);
}
}
在执行变量绑定时,非ref 模式下,会尝试把copy 原有变量的值,当然如果原有变量没有实现 Copy 则会执行移动语义
#[derive(Debug)]
struct Apple(i32);
fn main() {
let app = Some(Apple(10));
match app {
Some(a) => println!("move apple, apple not use"),
None=> (),
}
//println!("{:?}",app);//what will happen
let app = Some(Apple(10));
match app {
Some(ref a) => println!("borrow apple, apple can use"),
None=> (),
}
println!("{:?}",app);//what will happen
let mut app = Some(Apple(10));
match app {
Some(ref mut a) => a.0 = 100,
None=> (),
}
println!("{:?}",app);//what will happen
}
@ 绑定模式一般用于解构的同时 同时增加约束 希望绑定内部变量
#[derive(Debug)]
struct Apple(i32);
fn main() {
let app = Some(Apple(20));
match app {
Some(Apple(size @ 10)) => println!("apple size is 10 {}",size),
Some(Apple(size)) => println!("apple size is {}",size),
None=> (),
}
}
引用模式
引用模式可以实现对引用的解引用 关键字为 @@@@
#[derive(Debug)]
struct Apple(i32);
fn main() {
let app = Apple(20);
match &app {
&Apple(size @ 20) => println!("apple size is 20 {}",size),
_ => (),
&apple => println!("apple is {:?}",apple),
}
}
match匹配
在我们学习完模式的使用之后 在回头来看match,基本上就能够对match的使用有一个基本的认识了
match guard: 模式守卫允许在match 完成模式匹配之后 进一步改进匹配标准。 模式守卫出现在模式的后面,由关键字 if 后面的布尔类型表达式组成。
let message = match maybe_digit {
Some(x) if x < 10 => process_digit(x),
Some(x) => process_other(x),
None => panic!(),
};
练习
定义一个消息类型格式 如下
#[derive(Debug)]
enum Message {
Quit,
Move {x: i32, y: i32 },
Write(String),
}
需求为:
- 如果消息退出,打印
quit - 如果消息为移动:如果
x = 0 y = 0打印don't move - 如果消息为移动:如果
x > 0 y = 0打印move right - 如果消息为移动:如果
x < 0 y = 0打印move left - 如果消息为移动:如果
x = 0 y > 0打印move up - 如果消息为移动:如果
x = 0 y < 0, 打印move down - 如果消息为移动:不符合上述条件 打印坐标
- 如果消息为Write:打印内容(需要保证Message在 匹配完以后 依然可以使用)
#[derive(Debug)]
enum Message {
Quit,
Move {x: i32, y: i32 },
Write(String),
}
fn match_msg(message: &Message) {
}
fn main() {
let msg = Message::Quit;
match_msg(&msg);
let msg = Message::Move(0,0);
match_msg(&msg);
let msg = Message::Write("hello world".into());
match_msg(&msg);
println!("{}",msg);
}