模式匹配
模式:是rust中的一种特殊语法,用于匹配复杂和简单类型的结构。
将模式与匹配表达式和其他结构结合使用,可更好控制程序控制流。
模式由以下元素(及组合)组成:
- 字面值
- 解构的数组、enum、struct和tuple
- 变量
- 通配符
- 占位符
模式匹配的场景
match的Arm(分支)
格式:
match VALUE {
PATTERN => EXPRESSION,
PATTERN => EXPRESSION,
PATTERN => EXPRESSION,
}
match表达式的要求:详尽,分支需要包含所有的可能性。
一个特殊模式:_
(下划线)
- 会匹配任何内容
- 不会绑定到变量
- 常用于match的最后一个arm,或用于忽略某些值
match类似于swith语句,下划线类似于swith里的default语句。
条件if let表达式
if let表达式主要作为一种简短的方式来等价代替只有一个匹配项的match。
if let可选的跟随语句,可拥有else,包括:
- else if
- else if let
和match不同的是,if let不会检查穷尽性。
fn main() {
let f: Option<&str> = None;
let b = false;
let a: Result<u8, _> = "5".parse();
if let Some(c) = f {
println!("c={}", c);
} else if b {
println!("b={}", b);
} else if let Ok(d) = a {
if d > 3 {
println!("a > 3");
} else {
println!("a <= 3");
}
} else {
println!("other");
}
}
while let条件循环
只要模式继续满足匹配条件,则允许while循环一直运行。
fn main() {
let mut s = Vec::new();
s.push(1);
s.push(2);
s.push(3);
while let Some(t) = s.pop() {
println!("{}", t);
}
}
for循环
for循环是rust中最常见的循环,for循环中,模式就是紧随for关键字后的值。
fn main() {
let vs = vec!['a', 'b', 'c'];
for (i, v) in vs.iter().enumerate() {
println!("{},{}", i, v);
}
}
/*
0,a
1,b
2,c
*/
let语句
let语句也是模式,使用let PATTERN = EXPRESSION;
let a = 5; // 这种也是
let (x, y, z) = (1, 2, 3);
let (c, d) = (5, 6, 7); // 报错,个数不匹配
函数参数
fn fun1(x: i32) {}
fn fun2(&(x, y): &(i32, i32)) {
println!("{}, {}", x, y);
}
fn main() {
let p = (2, 3);
fun2(&p);
}
可辩驳性:模式是否会无法匹配
模式有两种:可失败的、不可失败的(或者叫 可辩驳的、无可辩驳的)
- 无可辩驳的:能匹配任何可能传递的值的模式,如let x=5;,肯定不会失败。
- 可辩驳的: 对于某些可能的值,无法进行匹配的模式,如if let Some(x) = val; 如果val为None时就无法匹配上。
语法分类:
- 函数参数、let语句、for循环只接受无可辩驳的模式
- if let和while let接受可辩驳和无可辩驳的模式,当接收无可辩驳的时候会发出警告,因可能失败
fn main() {
let a: Option<i32> = Some(5);
let Some(a) = a; // 编译报错refutable pattern in local binding,`let` bindings require an "irrefutable pattern"
}
意思就是let只能绑定到无可辩驳模式,但后面的Some存在可辩驳性。
fn main() {
let a: Option<i32> = Some(5);
if let Some(a) = a {} // 改成可辩驳模式,if let是可失败的
}
如果改成:
fn main() {
let a: Option<i32> = Some(5);
if let x = 5 {}
// 编译警告irrefutable `if let` pattern,
// this pattern will always match, so the `if let` is useless,
// consider replacing the `if let` with a `let`
}
无可辩驳的if let
模式,这个模式将永远匹配,所以if let
是没有用的,可以考虑用let
替换if let
。
对于match表达式,最后一个表达式应是无可辩驳的,其余表达式是可辩驳的。
模式语法
匹配字面值
模式可直接匹配字面值
fn main() {
let x = 2;
match x {
1 => println!("1"),
2 => println!("2"),
_ => println!("other"),
}
}
匹配命名变量
命名变量是可匹配任何值的无可辩驳模式
fn main() {
let x = Some(3);
let y = 10;
match x {
Some(5) => println!("x=5"),
Some(y) => println!("in match y={:?}", y),
_ => println!("default {:?}", x),
}
println!("go out match y={:?}", y);
}
/*输出
in match y=3
go out match y=10
*/
多重模式
在match表达式中,使用|
语法(或的意思)可匹配多种模式。
fn main() {
let x = 2;
match x {
1 | 2 => println!("1 or 2"), //表示1或2都能匹配上
3 => println!("3"),
_ => println!("other"),
}
}
使用..=匹配某个范围值
fn main() {
let x = 5;
match x {
1..=5 => println!("1 ~ 5"), //表示匹配1到5任意数字
_ => println!("other"),
}
let x = 'C';
match x {
'a'..='z' => println!("a ~ z"), //表示匹配a到z
'A'..='Z' => println!("A ~ Z"), //表示匹配A到Z
_ => println!("other"),
}
}
/*输出
1 ~ 5
A ~ Z*/
解构以分解值
可用模式来解构struct、enum、tuple,从而引用这些类型值的不同部分。
struct P { x: i32, y: i32, }
fn main() {
let p1 = P { x: 2, y: 3, };
let P { x: a, y: b} = p1; //创建a、b变量分别匹配x和y
println!("{},{}", a, b);
// 但上述写法比较冗余,可改成如下创建x y变量的写法
let P {x, y} = p1; // 同名后能够直接匹配,等同于 let P { x: x, y: y} = p1;
println!("{},{}", x, y);
// 还可使用match匹配
match p1 {
P { x, y: 0 } => println!("match x=any y=0, {},{}", x, y),// 匹配x任意,y=0
P { x: 0, y } => println!("match x=0 y=any, {},{}", x, y),// 匹配x=0,y任意
P { x, y } => println!("match x & y =any, {},{}", x, y),// 匹配x和y都任意
}
}
/*输出
2,3
2,3
match x & y =any, 2,3
*/
解构enum
enum Msg {
Q,
M { x: i32, y: i32, },
W (String),
C (i32, i32, i32),
}
fn main() {
let m = Msg::C(0, 1, 2);
match m {
Msg::Q => { println!("Q") },
Msg::M { x, y } => { println!("{},{}", x, y) },
Msg::W(t) => { println!("{}", t) },
Msg::C(a, b, c) => { println!("{},{},{}", a, b, c) },
}
}
解构嵌套的struct和enum
enum Cc {
C1 (i32, i32, i32),
C2 (i32, i32, i32),
}
enum Msg {
Q,
M { x: i32, y: i32, },
W (String),
C (Cc),
}
fn main() {
let m = Msg::C(Cc::C2(0, 1, 2));
match m {
Msg::Q => { println!("Q") },
Msg::M { x, y } => { println!("{},{}", x, y) },
Msg::W(t) => { println!("{}", t) },
Msg::C(Cc::C1(a, b, c)) => { println!("C1 {},{},{}", a, b, c) },
Msg::C(Cc::C2(a, b, c)) => { println!("C2 {},{},{}", a, b, c) },
}
}
//输出C2 0,1,2
解构struct和tuple
struct P { x: i32, y: i32, }
fn main() {
let ((f1, f2), P {x, y}) = ((1, 2), P {x: 3, y: 5, });
println!("{},{}, {},{}", f1, f2, x, y);//1,2, 3,5
}
在模式中忽略值
有几种可在模式中忽略整个值或部分值:_
、_配合其它模式
、使用以_开头的名称
、..忽略值的剩余部分
。
- 使用
_
忽略整个值
fn func(_: i32, y: i32) {
println!("{}", y);
}
fn main() {
func(2, 3);
}
- 使用嵌套的
_
忽略值的一部分
fn main() {
let mut a = Some(2);
let b = Some(3);
match (a, b) {
(Some(_), Some(_)) => { //匹配只要两个都是Some即可,内部的值并不关心
println!("all Some")
}
_ => { a = b; }
}
println!("a={:?}", a);
let v = (1, 2, 3, 4, 5);
match v { // 只匹配第1 3 5号元素
(n1, _, n3, _, n5) => println!("{},{},{}", n1, n3, n5),
}
}
/*
all Some
a=Some(2)
1,3,5
*/
- 使用
_
开头命名忽略未使用的变量
fn main() {
let _x = 5; // 未使用变量,下划线开头后忽略变量,编译时不会发生警告
let y = 6; // 不加且未使用则会发生警告
let s = Some(String::from("d"));
if let Some(_s) = s { println!("move s to _s") } //打印move s to _s
println!("s={:?}", s); //再使用s则报错,因为已失去所有权
//改使用_匹配则不会,可正常运行
if let Some(_) = s { println!("not move s to _s") }
println!("s={:?}", s);
}
- 使用
..
忽略值的剩余部分
struct P { x: i32, y: i32, z: i32, }
fn main() {
let p1 = P {x: 1, y: 2, z: 3};
match p1 {//匹配第一个
P {x, ..} => println!("{}", x),
}
let num = (2, 3, 4, 5, 6);
match num {
(f, .., l) => { // 匹配第一个和最后一个
println!("{},{}", f, l)
}
}
/*
1
2,6
*/
match num {
(.., f, ..) => { // 这种写法则会发生歧义,编译报错,编译器不知道取中间第几个
println!("{}", f)
}
}
}
使用 match 守卫来提供额外的条件
match 守卫就是 match arm 模式后额外的if条件,想要匹配该条件也必须满足。
match 守卫适用于比单独的模式更复杂的场景。
fn main() {
let b = Some(3);
match b {
Some(x) if x < 5 => { //匹配要求x小于5
println!("x={} < 5", x)
}
Some(x) => println!("x={}", x),
None => (),
}
}
// x=3 < 5
另一个例子
fn main() {
let a = Some(10);
let b = 10;
match a {
Some(r) if r == b => { //if a == b不是模式,不会引入新的变量
println!("r={} a==b", r)
}
Some(20) => println!("50"),
_ => println!("a={:?}", a),
}
}
// r=10 a==b
多重匹配例子:
fn main() {
let a = 5;
let b = true;
match a { //使用多重模式,且b为true
3 | 5 | 7 if b => println!("yes"), // 输出yes
_ => println!("no"),
}
}
@绑定
@符号使得可创建一个变量,该变量可在测试某个值是否与模式匹配的同时保存该值。
#[derive(Debug)]
enum M { H { i: i32 }, }
fn main() {
let a = M::H { i: 5 };
match a {
M::H { // 匹配后,并将i赋给i_v变量
i: i_v @ 3..=7,
} => println!("i_v={}", i_v),
// M::H { i: 10..=15 } => println!("i={:?} 10~15", i), //试图直接使用i,报错cannot find value `i` in this scope,所以要有上一行的@
M::H { i: 10..=15 } => println!("a={:?} 10~15", a),
M::H { i } => println!("i={}", i),
}
}
// 输出i_v=5
标签:fn,i32,30,Some,let,rust,println,match,模式匹配
From: https://www.cnblogs.com/UFO-blogs/p/17863045.html