rust 模式匹配 pattern match
1 概念逻辑
Important
- 模式匹配是一种编程语言特性, 允许你将值(简单的值或者复杂的数据结构)与预定义的模式进行比较,并在匹配成功时执行相应的代码(变量绑定或其他操作)
- 结合下图, 然后后面的实际例子, 体会一下
- 就我个人印象, 了解下模式匹配的定义(没错,就是定义) 是很有好处的, 之前就直接开干,概念没有特别去思考, 有些东西会有些模糊
2 let
let PATTERN = EXPRESSION;
fn main() {
// 这种变量申明初始化, 其实就是模式匹配
// x 就是pattern , 然后1 就是 x 要与之匹配的值
// 这个就属于 irrefutable 类型的模式, 因为x 能匹配上任何东西, 必定匹配上的.
let x = 1;
// (b,c) 是模式, 右边的(1,2) 是值, 结构上能够匹配上
// 所以 将 1 绑定到变量b, 将 2 绑定到变量c
let (b, c) = (1, 2);
println!("{}-{}", b, c);
// 忽略第一个元素 , 这里 _ 就是图一里提到的占位符模式
let (_, y) = (1, 2);
println!("y: {}", y);
struct Person {
name: String,
age: i32,
}
let p1 = Person {
name: "zhang fei".to_string(),
age: 22,
};
// 同理
let Person { name: n, age: m } = p1;
println!("{} : {}", n, m);
let p2 = Person {
name: "liu bei".to_string(),
age: 32,
};
// 名字与字段名一样的情况,可以省略
let Person { name, age } = p2;
println!("{} : {}", name, age);
let p3 = Person {
name: "guan yu".to_string(),
age: 13,
};
// 可以使用.. 省略
let Person { name,.. } = p3;
println!("{}",name);
}3 match
3.1 枚举
模式是枚举
fn main() {
let x = Some(5);
// 这里的 x 是值
match x {
// Some(value) 是模式
Some(value) => {
// 匹配成功后,执行相应代码
// Some(value)=Some(5) 这里也绑定了 变量, value=5
println!("Got a value: {}", value)
}
// None 是模式
None => println!("No value"),
}
}enum Coin {
Penny,
Nickel,
Dime(String),
Quarter,
}
fn main() {
let c = Coin::Penny;
let d = Coin::Dime("hello".to_string());
// 这里 let r= 就是前面我们提到 irrefutable 类型的模式
// 然后右边本身也含着一个 模式匹配
let r = match d {
Coin::Penny => {
println!("Lucky penny!");
1
}
Coin::Nickel => 5,
Coin::Dime(s) => {
println!("{}", s); // 可以在这边获取枚举成员携带的数据
10 //返回值
}
// 必须把所有成员匹配都写上,否则报错, 去看 Pattern Syntax
Coin::Quarter => 25,
};
println!("{}", r);
}3.2 字面量
fn main(){
let x = 1;
match x {
// 这里的数字1 就是模式, 是字面量的模式
1 => println!("一"),
2 => println!("二"),
// 虽然这个也匹配, 但是只会执行前面第一个匹配的分支
1 => println!("1"),
3 => println!("三"),
4 => println!("四"),
5 => println!("五"),
// _ 是占位符模式
_ => println!("其他数字"),
}
match x{
2=>println!("二"),
// 可以随便一个变量名来表示其他数据
other=>println!("其他数字: {}",other),
}
}3.3 结构体
struct Point {
x: i32,
y: i32,
}
fn main() {
let p = Point { x: 10, y: 0 };
match p {
Point { x, y: 0 } => println!("y=0 才能匹配上 {}", x),
Point { x, y: 0..=2 } => println!("y>=0 <=2 是才匹配上 {}", x),
Point { x: 0, y } => println!("x=0 才能匹配上 {}", y),
Point { x, y } => println!("都能匹配上 ({}, {})", x, y),
}
}4 if let
相当于 使用 match ,然后只匹配一个成员,然后执行代码块, 更加简洁
fn main() {
// Some 这个是Option<t> 枚举里的成员, 因为 默认 use了,所以可以直接拿来用
let config_max = Some(3u8);
match config_max {
Some(max) => println!("The maximum is configured to be {}", max),
_ => (),
}
let config_max = Some(3u8);
// 相当于上面的 match, 但是我们这里不用写 另外 _ 的分支
// 模式匹配上,绑定值到变量max, 然后执行 {} 里的代码, 匹配不上 就继续后面的代码
// 和if 判断 很像
if let Some(max) = config_max {
println!("The maximum is configured to be {}", max);
}
}5 while let
fn main() {
let mut optional = Some(0);
// let Some(i)=optional 匹配上, 就进入{}
// 1. 第一次 匹配上 i=0
while let Some(i) = optional {
if i > 9 {
println!("Greater than 9, quit!");
optional = None;
} else {
// 2. 到这里, ... 到 i=9 最后一次在这里,然后 i>9,走上面的分支
println!("`i` is `{:?}`. Try again.", i);
optional = Some(i + 1);
}
}
}6 let ref
ref 是模式的一部分, 而 & 是借用运算符,是表达式的一部分
看一下这个例子, 报错了
我们使用ref 借用name就行
报错: 枚举中发生move
添加ref
7 let mut
Caution
没错, 我们平常使用的let mut x=1 类似这种语句, mut 是模式匹配中模式的一部分
struct Person {
name: String,
age: i32,
}
fn main() {
let mut x=2;
let (mut a, mut b) = (1, 2);
a = 33;
println!("{}", a);
struct Person {
name: String,
age: i32,
}
let p1 = Person {
name: "zhang fei".to_string(),
age: 22,
};
let Person { name, age } = p1;
println!("{} : {}", name, age);
// name.push_str("string"); 报错,因为name 不可变
let p1 = Person {
name: "zhang fei".to_string(),
age: 22,
};
let Person { mut name, age } = p1;
name.push_str("string"); // ok
}8 Pattern Syntax
8.1 .. 剩余
8.2 _ 忽略绑定
8.3 _ 剩余部分
成员不用全部写上的方式
enum Coin {
Penny,
Nickel,
Dime(String),
Quarter,
}
fn main() {
let d = Coin::Dime("hello".to_string());
let r = match d {
Coin::Penny => {
println!("Lucky penny!");
1
}
// 用other 表示其他情况
other => 666,
};
println!("{}", r);
}使用_来表示不匹配的情况,这里的和枚举显然不一样,在枚举里如果要写,是肯定能匹配上的,这里是完全可能匹配不上,所以用 _ 来区分 前面用的other.
其实都用 _ 就行
8.4 | 和 if
8.5 ..= 范围
>=xx <=yy
8.6 @ 绑定
enum Student {
primary { age: i32 },
junior { age: i32 },
}
fn main() {
let s = Student::primary { age: 3 };
match s {
Student::primary { age: primary_age } if primary_age >= 7 && primary_age <= 11 => {
println!("Hello")
}
// 相当于上面 primary_age >= 3 && primary_age <= 6
// @前面是绑定的变量
Student::primary {
// 这里就表示当你符合 >=3 <=6 ,绑定匹配到的age到primary_age 这个变量
age: primary_age @ 3..=6,
} => println!("是 >=3 <=6 岁的小学生: {}", primary_age),
Student::junior { age: 10..=12 } => {
println!("10-12 岁的初中生")
}
Student::junior { age } => println!("Found: {}", age),
_ => (),
}
let a=Some(42);
match a {
// 得到 `Some` 可变类型,如果它的值(绑定到 `n` 上)等于 42,则匹配。
Some(n @ 42) => println!("The Answer: {}!", n),
// 匹配任意其他数字。
Some(n) => println!("Not interesting... {}", n),
// 匹配任意其他值(`None` 可变类型)。
_ => (),
}
}9 non_exhaustive
Caution
- 当你使用上游开发人员创建的一个enum, 然后你写了全部分支, OK.
- 突然有一天, 上游人员给这个enum 添加了一个变体成员, 这个时候因为你的模式匹配enum中没有写_ 这种,导致现在没有写完全部分支,错误发生了
- non_exhaustive就是为了防止这种情况的发生而设计的
use world::enum_ex::Coin;
fn main() {
let c = Coin::Penny;
let d = Coin::Dime("hello".to_string());
let r = match d {
Coin::Penny => {
println!("Lucky penny!");
1
}
Coin::Nickel => 5,
Coin::Dime(s) => {
println!("{}", s);
10
}
Coin::Quarter => 25,
// 必须 使用 _, 否则报错,编译不过的
_ ==> 0,
};
println!("{}", r);
}