1 使用模式的场合
1.1 match
分支
- match表达式必须是穷尽的(exhaustive)
match VALUE {
PATTERN => EXPRESSION,
PATTERN => EXPRESSION,
PATTERN => EXPRESSION,
}
1.2 if let
条件表达式
- 可组合使用
if let/else if/else if let
,各分支的条件不一定要相互关联 - 可以在分支模式中引入仅在分支中有效的覆盖变量
if let
表达式对于模式匹配不必是穷尽的,这是与match
表达式的差别
fn main() {
let favorite_color: Option<&str> = None;
let is_tuesday = false;
let age: Result<u8, _> = "34".parse();
if let Some(color) = favorite_color {
println!("Using your favorite color, {}, as the background", color);
} else if is_tuesday {
println!("Tuesday is green day!");
// 这里引入了覆盖变量 age
} else if let Ok(age) = age {
if age > 30 {
println!("Using purple as the background color");
} else {
println!("Using orange as the background color");
}
} else {
println!("Using blue as the background color");
}
}
1.3 while let
条件循环
let mut stack = Vec::new();
stack.push(1);
stack.push(2);
stack.push(3);
while let Some(top) = stack.pop() {
println!("{}", top);
}
1.4 for
循环
let v = vec!['a', 'b', 'c'];
for (index, value) in v.iter().enumerate() {
println!("{} is at index {}", value, index);
}
1.5 let
语句
let
语句的正式语法:let PATTERN = EXPRESSION;
- 这也是模式:
let x = 5;
- 元组解构:
let (x, y, z) = (1, 2, 3);
- 可以用
_
或者..
忽略部分值
1.6 函数参数
fn print_coordinates(&(x, y): &(i32, i32)) {
println!("Current location: ({}, {})", x, y);
}
fn main() {
let point = (3, 5);
print_coordinates(&point);
}
2 可反驳性(refutability)
- 不可反驳的(irrefutable):可接受传入的任何值,模式总是匹配的,如
let x = 5;
- 可反驳的(refutable):对于某些值,模式不匹配,如
let Some(x) = value
中的模式Some(x)
,如果传入的值value
是None
,则模式不匹配 - 通常不用特别注意可反驳与不可反驳
- 仅可接受不可反驳模式的场合
- 仅可接受可反驳模式的场合
3 模式语法
3.1 匹配
3.1.1 匹配字面值
let x = 1;
match x {
1 => println!("one"),
2 => println!("two"),
3 => println!("three"),
_ => println!("anything"),
}
3.1.2 匹配命名变量
fn main() {
let x = Some(5);
let y = 10;
match x {
Some(50) => println!("Got 50"),
Some(y) => println!("Matched, y = {:?}", y),
_ => println!("Default case, x = {:?}", x),
}
println!("at the end: x = {:?}, y = {:?}", x, y);
}
3.1.3 多个模式
let x = 1;
match x {
1 | 2 => println!("one or two"),
3 => println!("three"),
_ => println!("anything"),
}
3.1.4 用...
匹配范围
let x = 5;
match x {
1 ... 5 => println!("one through five"),
_ => println!("something else"),
}
let x = 'c';
match x {
'a' ... 'j' => println!("early ASCII letter"),
'k' ... 'z' => println!("late ASCII letter"),
_ => println!("something else"),
}
3.2 解构
3.2.1 解构结构体
3.2.1.1 示例1:通常用法
struct Point {
x: i32,
y: i32,
}
fn main() {
let p = Point { x: 0, y: 7 };
// 注意:结构体字段名在前,匹配变量名在后
let Point { x: a, y: b } = p;
assert_eq!(0, a);
assert_eq!(7, b);
}
3.2.1.2 示例2:变量名与结构体字段名相同时可省略
struct Point {
x: i32,
y: i32,
}
fn main() {
let p = Point { x: 0, y: 7 };
// Point{x:x,y:y} 简写成了 Point{x,y}
let Point { x, y } = p;
assert_eq!(0, x);
assert_eq!(7, y);
}
3.2.1.3 示例3:可规定某些字段的值
fn main() {
let p = Point { x: 0, y: 7 };
match p {
Point { x, y: 0 } => println!("On the x axis at {}", x),
Point { x: 0, y } => println!("On the y axis at {}", y),
Point { x, y } => println!("On neither axis: ({}, {})", x, y),
}
}
3.2.2 解构枚举
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!("The Quit variant has no data to destructure.")
},
Message::Move { x, y } => {
println!("Move in the x direction {} and in the y direction {}",x,y);
}
Message::Write(text) => println!("Text message: {}", text),
Message::ChangeColor(r, g, b) => {
println!("Change the color to red {}, green {}, and blue {}",r,g,b)
}
}
}
3.2.3 解构引用
let points = vec![
Point { x: 0, y: 0 },
Point { x: 1, y: 5 },
Point { x: 10, y: -3 },
];
let sum_of_squares: i32 = points
.iter()
.map(|&Point { x, y }| x * x + y * y)
.sum();
- 向量
points
含有Point
类型的值,但是进行迭代时候得到的是各个元素的引用,即&Point
类型的值 - 原文说漏掉
&
会出类型不匹配的编译错误,但在1.27.1版本中验证,不要&
也可以编译通过,估计与Point
类型实现了Clone
特性有关
3.2.4 解构模式组合
let ((feet, inches), Point {x, y}) = ((3, 10), Point { x: 3, y: -10 });
3.3 特殊语法
3.3.1 用_
忽略值
3.3.1.1 示例1:忽略整个值
fn foo(_: i32, y: i32) {
println!("This code only uses the y parameter: {}", y);
}
fn main() {
foo(3, 4);
}
3.3.1.2 示例2:忽略部分值
let mut setting_value = Some(5);
let new_setting_value = Some(10);
match (setting_value, new_setting_value) {
(Some(_), Some(_)) => {
println!("Can't overwrite an existing customized value");
}
_ => {
setting_value = new_setting_value;
}
}
println!("setting is {:?}", setting_value);
3.3.1.3 示例3:忽略多个值
let numbers = (2, 4, 8, 16, 32);
match numbers {
(first, _, third, _, fifth) => {
println!("Some numbers: {}, {}, {}", first, third, fifth)
},
}
3.3.1.4 示例4:忽略未使用的变量
fn main() {
let _x = 5; // 忽略未使用的变量
}
let s = Some(String::from("Hello!"));
// 注意:_s 仍然会进行变量绑定,s的值仍然会移动到_s中,这样,后面使用s的语句会出错:值已经移动,无法使用
// 注意:如果仅使用_,则仅仅忽略值,不会进行变量绑定,s的值不会被移动,后面使用s的语句不会出错
if let Some(_s) = s {
println!("found a string");
}
println!("{:?}", s);
3.3.2 用..
忽略值
3.3.2.1 示例1:用..
忽略其他值
struct Point {
x: i32,
y: i32,
z: i32,
}
let origin = Point { x: 0, y: 0, z: 0 };
match origin {
// 忽略除x之外的其他字段
Point { x, .. } => println!("x is {}", x),
}
3.3.2.2 示例2:..
会自动扩展匹配必需的值的数量
fn main() {
let numbers = (2, 4, 8, 16, 32);
match numbers {
// .. 会自动扩展匹配必需的值的数量:这里first,last分别匹配第一个和最后一个值
(first, .., last) => {
println!("Some numbers: {}, {}", first, last);
},
}
}
3.3.2.3 示例3:使用..
时不能有歧义
fn main() {
let numbers = (2, 4, 8, 16, 32);
match numbers {
// 这里有歧义,不能通过编译:前后的..应该分别匹配多少个值?
(.., second, ..) => {
println!("Some numbers: {}", second)
},
}
}
3.3.3 用ref
和ref mut
创建引用
- 模式使用的变量将绑定一个值,外部变量的所有权将转移到模式变量中,这可能会导致问题
- 此时使用解构引用(见上文)的方法是不行的,因为被匹配的值不是引用
- 此时应该使用
ref
或者ref mut
,表示取得被匹配值的引用
let robot_name = Some(String::from("Bors"));
match robot_name {
// 1 robot_name携带的String变量的所有权被转移到模式变量name中
// 后面使用robot_name的语句将出错:所有权已经转移,变量已经失效
// 2 此时使用&name是不行的:被匹配的值不是引用,将出现类型不匹配的编译错误
// 3 此时应该使用ref name,表示获取被匹配值的引用
// 4 如果要修改被匹配值,则使用ref mut name,然后在对应的表达式中修改被匹配的值: Some(ref mut name) => *name = String::from("Another name"),
Some(name) => println!("Found a name: {}", name),
None => (),
}
println!("robot_name is: {:?}", robot_name);
3.3.4 匹配守卫(match guard)
let num = Some(4);
match num {
Some(x) if x < 5 => println!("less than five: {}", x),
Some(x) => println!("{}", x),
None => (),
}
- 匹配守卫:match分支模式之后的额外if条件,守卫条件必须也被满足才可以选择相应的分支。
- 注意次序:如果上面例子中第二个分支变成第一个,则新的第二个分支编程不可到达的分支,从而出现编译错误
let num = Some(4);
match num {
Some(x) => println!("{}", x),
// 这里会出编译错误:不可到达的分支
Some(x) if x < 5 => println!("less than five: {}", x),
None => (),
}
- 匹配守卫作用于分支中的整个模式。下面例子中的
4 | 5 | 6 if y
等价于(4 | 5 | 6) if y
,而不是4 | 5 | (6 if y)
let x = 4;
let y = false;
match x {
4 | 5 | 6 if y => println!("yes"),
_ => println!("no"),
}
3.3.5 @
绑定
@
绑定:绑定变量并且限定变量值必须满足的模式- 注意:
@
后面必须跟随模式,而不能跟随条件守卫
enum Message {
Hello { id: i32 },
}
let msg = Message::Hello { id: 5 };
match msg {
//也可以写成:Message::Hello { id: idval } if 3 <= idval && idval <= 7 => {
Message::Hello { id: idval @ ..7} => {
println!("Found an id in range: {}", idval)
},
// 没有绑定变量,仅限定字段值范围
Message::Hello { id: 10...12 } => {
println!("Found an id in another range")
},
Message::Hello { id } => {
println!("Found some other id: {}", id)
},
}