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) },}