模式

模式是Rust中特殊的语法,用来匹配值的结构
组成:
(1) 字面值
(2) 解构的数组、枚举、结构体或者元组
(3) 变量
(4) 通配符
(5) 占位符

match的例子

match必须匹配完所有的情况

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//1.match
//match VALUE {
// PATTERN => EXPRESSION,
// PATTERN => EXPRESSION,
// PATTERN => EXPRESSION,
// }
fn main() {
let a = 1;
match a {
0 => println!("Zero"),
1 => println!("One"),
_ => println!("other value"),
};

}

if let

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
fn main() {
let color: Option<&str> = None;
let is_ok = true;
let age: Result<u8,_> = "33".parse();

if let Some(c) = color {
println!("color = {}",c);
} else if is_ok {
println!("is ok");
}else if let Ok(a) = age {
if a > 30 {
println!("oh,mature man");
}else {
println!("oh,young man");
}
}else {
println!("is else");
}

}

while let

只要模式匹配就一直执行while循环

1
2
3
4
5
6
7
8
9
10
11
fn main() {
let mut stack = Vec::new();
stack.push(1);
stack.push(2);
stack.push(3);

while let Some(top) = stack.pop() {
println!("top = {}",top);
}//相当于清空栈

}

for

在for循环中模式是直接跟随for循环字
此处的模式是(index,value)

1
2
3
4
5
6
fn main() {
let v = vec!['a','b','c'];
for (index,value) in v.iter().enumerate() {
println!("index: {},value {}",index,value);
}
}

let语句

1
2
3
4
5
6
7
8
//let PATTERN = EXPRESSION
fn main() {
let (x,y,z) = (1,2,3); //(1,2,3)会匹配(x,y,z),将1绑定到x,2绑定到y,3绑定到z
println!("{},{},{}",x,y,z);

let (x,..,z) = (1,2,3);
println!("{},{}",x,z);
}

函数的模式

函数的参数也是模式

1
2
3
4
5
6
7
8
9
fn print_point (&(x,y): &(i32,i32)) {
println!("x = {},y = {}",x,y);
}

fn main() {
let p = (3,5);
print_point(&p);
}
//模式在使用它的地方并不都是相同的,存在不可反驳的和可反驳的

模式的可反驳和不可反驳

模式有两种:refutable 和irrefutable(可反驳的和不可反驳的),能够匹配任何传递的可能值
的模式叫做不可反驳模式,对值匹配可能失败的叫做可反驳模式

  1. 只能接受不可反驳模式的由: 函数、let语句、for循环,因为其通过不匹配的值程序无法进行有意义工作
  2. if let和while let表达式被限制为只能接受可反驳的模式,因为他们定义就是为了处理有可能失败的条件
1
2
3
4
5
6
7
8
9
10
fn main() {
let a: Option<i32> = Some(5); //匹配Some(value)或者None
let b: Option<i32> = None; //匹配Some(value)或者None
//let Some(x) = a;//报错,只接受不可反驳模式

if let Some(v) = a {
//if let v = 5 { //没有报错,但是会警告
println!("v = {}",v);
}
}

匹配字面值

1
2
3
4
5
6
7
8
fn main() {
let x = 1;
match x {
1 => println!("one"),
2 => println!("two"),
- => println!("xx"),
};
}

匹配命名变量

1
2
3
4
5
6
7
8
9
10
11
fn main() {
let x = Some(5);
let y = 10;//一位置
match x {
Some(50) => println!("50"),
Some(y) => println!("y = {}",y), //这个y和之前的y不同,是局部变量,屏蔽之前的y
_ => println!("other"),
};

println!("x = {:?},y = {:?}",x,y);//是1位置的y
}

多个模式

1
2
3
4
5
6
7
8
fn main() {
let x = 1;
match x {
1|2 => println!("1 or 2"), //匹配1或者是2
3 => println!("3"),
- => println!("xx"),
};
}

通过…匹配

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
fn main() {
let x = 5;

match x {
1..=5 => println!("1 to 5"), //1|2|3|4|5
//等价于
//1|2|3|4|5 => println!("1 to 5"),
- => println!("xx"),
};
let x = 'c';
match x {
'a'..='j' => println!("1"),
'k'..='z' => println!("2"),
_ => println!("other"),
};

}

解构并分解值

解构元组,结构体、枚举、引用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
struct Point {
x: i32,
y: i32,
}

fn main() {

//两种方法
//a,b匹配x,y
let p = Point {x: 1,y: 2};
let Point{x:a,y:b} = p;
let Point{x:x,y:y} = p;//同名省略

assert_eq!(1,x);
assert_eq!(1,y);
assert_eq!(1,a);
assert_eq!(2,b);
}
  • 使用字面值解构
1
2
3
4
5
6
7
let p = Point {x:1,y:0};
match p {
Point {x=0,y=0} => println!("原点"),
Point {x,y:0} => println!("x axis"),
Point{x=0,y} => println!("y axis"),
Point {x,y} => println!("other"),
};
  • 解构枚举结构体
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
enum Message {
Quit,
Move{x: i32,y: i32},
Write(String),
ChangeColor{i32,i32,i32},
}
fn main() {
let msg = Message::ChangeColor(0,160,255);

match msg {
Message::Quit => {
println!("quit");
},
Message::Move{x,y} => {println!("move x {} y {}",x,y);},
Message::Write(text) => println!("write msg = {}",text),
Message::ChangeColor(r,g,b) => {
println!("color, r = {},g = {},b= {}",r,g,b);
},
};

}
  • 解构嵌套的结构体和枚举
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
enum Color {
Rgb(i32,i32,i32),
Hsv(i32,i32,i32),
}
enum Message {
Quit,
Move{x: i32,y: i32},
Write(String),
ChangeColor(Color),
}

fn main() {
let msg = Message::ChangeColor(Color::Hsv(0,160,255));

match msg {
Message::ChangeColor(Color::Rgb(r,g,b)) => {
println!("rgb color, r = {},g= {},b = {}",r,g,b);
},
Message::ChangeColor(Color::Rgb(r,g,b)) => {
println!("hsv color, h = {},s= {},v = {}",h,s,v);
},
_ => () //do nothing
};

}
  • 解构结构体和元组
1
2
3
4
5
6
7
8
9
struct Point {
x: i32,
y: i32,
}

fn main() {
let ((a,b),Point{x,y}) = ((1,2),Point{x: 3,y: 4}); //解构完成
//a = 1,b = 2,x = 3,y = 4
}

忽略模式中的值

可能使用下划线的场景

1
2
3
4
5
6
7
8
9
10
11
trait A {
fn bar(x :i32,y: i32);
}
struct B {

}
impl A for B {
fn bar(_ :i32,y: i32) {
println!("y = {}",y);
}
}
  • 使用下划线忽略整个值
1
2
3
4
5
6
7
fn foo (_ : i32,y: i32) {
println!("y = {}",y);
}

fn main() {
foo(1,2);
}
  • 使用下划线忽略一部分值
1
2
3
4
5
6
7
8
fn main() {
let numbers = (1,2,3,4);
match numbers {
(one,_,three,_) => {
println!("one {},three {}",one,three);
},
}
}
  • 在变量名字前使用下划线忽略未使用的变量

使用下划线后,未使用的变量会不警告

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
fn main() {
let _x = 5;
let _y = 5;

let s = Some(String::from("hello"));
if let Some(_c) = s { //只是看有没有具体的值,而不考虑具体值的类型
println!("found a string");
}
//报错
//在Some处被借用,不管有没有下划线
//println!("s = {}",s);

let s = Some(String::from("hello"));
if let Some(_) = s { //只是看有没有具体的值,而不考虑具体值的类型
println!("found a string");
}
println!("s = {}",s);//此时不会报错,没有发生所有权的移动
}
  • 使用…忽略大部分值

使用…匹配的过程中一定不能出现歧义
如错误例子:

1
2
3
4
5
match numbers {
(..,seconds,..) => {
println!("second = {}",second);
}
}
1
2
3
4
5
6
7
8
fn main() {
let numbers = (1,2,3,4,5,6,7);
match numbers {
(first,..,last) => {
println!("first {},last {}",first,last);
},
};
}

匹配守卫提供额外的条件

匹配守卫是一个指定于match分支模式之后,如果想要使用额外的if条件,必须满足这个分支
这种方法不需要引进新的变量

  • 匹配守卫方法1
1
2
3
4
5
6
7
8
fn main() {
let num = Some(4);
match num {
Some(x) if x < 5 => println!("<5"),
Some(x) => println!("x {}",x),
None => (),
};
}
  • 匹配守卫方法2
1
2
3
4
5
6
7
8
9
fn main() {
let num = Some(4);
let y = 10;//位置1
match num {
Some(x) if x == y => println!("num ==y"),//此处为位置1的y
Some(x) => println!("x {}",x),
None => (),
};
}
  • 匹配守卫方法3
1
2
3
4
5
6
7
8
fn main() {
let x = 4;
let y = false;
match x {
4|5|6 if y => println!("1"), //等价于(4|5|6) if y
_ => println!("2"),
};
}

使用@运算符