1 使用模式的场合

1.1 match分支

  • match表达式必须是穷尽的(exhaustive)
  1. match VALUE {
  2. PATTERN => EXPRESSION,
  3. PATTERN => EXPRESSION,
  4. PATTERN => EXPRESSION,
  5. }

1.2 if let条件表达式

  • 可组合使用if let/else if/else if let,各分支的条件不一定要相互关联
  • 可以在分支模式中引入仅在分支中有效的覆盖变量
  • if let表达式对于模式匹配不必是穷尽的,这是与match表达式的差别
  1. fn main() {
  2. let favorite_color: Option<&str> = None;
  3. let is_tuesday = false;
  4. let age: Result<u8, _> = "34".parse();
  5. if let Some(color) = favorite_color {
  6. println!("Using your favorite color, {}, as the background", color);
  7. } else if is_tuesday {
  8. println!("Tuesday is green day!");
  9. // 这里引入了覆盖变量 age
  10. } else if let Ok(age) = age {
  11. if age > 30 {
  12. println!("Using purple as the background color");
  13. } else {
  14. println!("Using orange as the background color");
  15. }
  16. } else {
  17. println!("Using blue as the background color");
  18. }
  19. }

1.3 while let条件循环

  1. let mut stack = Vec::new();
  2. stack.push(1);
  3. stack.push(2);
  4. stack.push(3);
  5. while let Some(top) = stack.pop() {
  6. println!("{}", top);
  7. }

1.4 for循环

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

1.5 let语句

  • let语句的正式语法:let PATTERN = EXPRESSION;
  • 这也是模式:let x = 5;
  • 元组解构:let (x, y, z) = (1, 2, 3);
  • 可以用_或者..忽略部分值

    1.6 函数参数

  1. fn print_coordinates(&(x, y): &(i32, i32)) {
  2. println!("Current location: ({}, {})", x, y);
  3. }
  4. fn main() {
  5. let point = (3, 5);
  6. print_coordinates(&point);
  7. }

2 可反驳性(refutability)

  • 不可反驳的(irrefutable):可接受传入的任何值,模式总是匹配的,如let x = 5;
  • 可反驳的(refutable):对于某些值,模式不匹配,如let Some(x) = value中的模式Some(x),如果传入的值valueNone,则模式不匹配
  • 通常不用特别注意可反驳与不可反驳
  • 仅可接受不可反驳模式的场合
    • let语句
    • for循环
    • 函数参数
  • 仅可接受可反驳模式的场合
    • if let表达式
    • while let表达式

3 模式语法

3.1 匹配

3.1.1 匹配字面值

  1. let x = 1;
  2. match x {
  3. 1 => println!("one"),
  4. 2 => println!("two"),
  5. 3 => println!("three"),
  6. _ => println!("anything"),
  7. }

3.1.2 匹配命名变量

  • 注意:覆盖外层同名变量时,仅在单个分支内有效
  1. fn main() {
  2. let x = Some(5);
  3. let y = 10;
  4. match x {
  5. Some(50) => println!("Got 50"),
  6. Some(y) => println!("Matched, y = {:?}", y),
  7. _ => println!("Default case, x = {:?}", x),
  8. }
  9. println!("at the end: x = {:?}, y = {:?}", x, y);
  10. }

3.1.3 多个模式

  1. let x = 1;
  2. match x {
  3. 1 | 2 => println!("one or two"),
  4. 3 => println!("three"),
  5. _ => println!("anything"),
  6. }

3.1.4 用...匹配范围

  1. let x = 5;
  2. match x {
  3. 1 ... 5 => println!("one through five"),
  4. _ => println!("something else"),
  5. }
  6. let x = 'c';
  7. match x {
  8. 'a' ... 'j' => println!("early ASCII letter"),
  9. 'k' ... 'z' => println!("late ASCII letter"),
  10. _ => println!("something else"),
  11. }
  • 范围仅能用于整数和字符类型

3.2 解构

3.2.1 解构结构体

3.2.1.1 示例1:通常用法

  1. struct Point {
  2. x: i32,
  3. y: i32,
  4. }
  5. fn main() {
  6. let p = Point { x: 0, y: 7 };
  7. // 注意:结构体字段名在前,匹配变量名在后
  8. let Point { x: a, y: b } = p;
  9. assert_eq!(0, a);
  10. assert_eq!(7, b);
  11. }

3.2.1.2 示例2:变量名与结构体字段名相同时可省略

  1. struct Point {
  2. x: i32,
  3. y: i32,
  4. }
  5. fn main() {
  6. let p = Point { x: 0, y: 7 };
  7. // Point{x:x,y:y} 简写成了 Point{x,y}
  8. let Point { x, y } = p;
  9. assert_eq!(0, x);
  10. assert_eq!(7, y);
  11. }

3.2.1.3 示例3:可规定某些字段的值

  1. fn main() {
  2. let p = Point { x: 0, y: 7 };
  3. match p {
  4. Point { x, y: 0 } => println!("On the x axis at {}", x),
  5. Point { x: 0, y } => println!("On the y axis at {}", y),
  6. Point { x, y } => println!("On neither axis: ({}, {})", x, y),
  7. }
  8. }

3.2.2 解构枚举

  1. enum Message {
  2. Quit,
  3. Move { x: i32, y: i32 },
  4. Write(String),
  5. ChangeColor(i32, i32, i32),
  6. }
  7. fn main() {
  8. let msg = Message::ChangeColor(0, 160, 255);
  9. match msg {
  10. Message::Quit => {
  11. println!("The Quit variant has no data to destructure.")
  12. },
  13. Message::Move { x, y } => {
  14. println!("Move in the x direction {} and in the y direction {}",x,y);
  15. }
  16. Message::Write(text) => println!("Text message: {}", text),
  17. Message::ChangeColor(r, g, b) => {
  18. println!("Change the color to red {}, green {}, and blue {}",r,g,b)
  19. }
  20. }
  21. }

3.2.3 解构引用

  1. let points = vec![
  2. Point { x: 0, y: 0 },
  3. Point { x: 1, y: 5 },
  4. Point { x: 10, y: -3 },
  5. ];
  6. let sum_of_squares: i32 = points
  7. .iter()
  8. .map(|&Point { x, y }| x * x + y * y)
  9. .sum();
  • 向量points含有Point类型的值,但是进行迭代时候得到的是各个元素的引用,即&Point类型的值
  • 原文说漏掉&会出类型不匹配的编译错误,但在1.27.1版本中验证,不要&也可以编译通过,估计与Point类型实现了Clone特性有关

3.2.4 解构模式组合

  1. let ((feet, inches), Point {x, y}) = ((3, 10), Point { x: 3, y: -10 });

3.3 特殊语法

3.3.1 用_忽略值

3.3.1.1 示例1:忽略整个值

  1. fn foo(_: i32, y: i32) {
  2. println!("This code only uses the y parameter: {}", y);
  3. }
  4. fn main() {
  5. foo(3, 4);
  6. }

3.3.1.2 示例2:忽略部分值

  1. let mut setting_value = Some(5);
  2. let new_setting_value = Some(10);
  3. match (setting_value, new_setting_value) {
  4. (Some(_), Some(_)) => {
  5. println!("Can't overwrite an existing customized value");
  6. }
  7. _ => {
  8. setting_value = new_setting_value;
  9. }
  10. }
  11. println!("setting is {:?}", setting_value);

3.3.1.3 示例3:忽略多个值

  1. let numbers = (2, 4, 8, 16, 32);
  2. match numbers {
  3. (first, _, third, _, fifth) => {
  4. println!("Some numbers: {}, {}, {}", first, third, fifth)
  5. },
  6. }

3.3.1.4 示例4:忽略未使用的变量

  1. fn main() {
  2. let _x = 5; // 忽略未使用的变量
  3. }
  1. let s = Some(String::from("Hello!"));
  2. // 注意:_s 仍然会进行变量绑定,s的值仍然会移动到_s中,这样,后面使用s的语句会出错:值已经移动,无法使用
  3. // 注意:如果仅使用_,则仅仅忽略值,不会进行变量绑定,s的值不会被移动,后面使用s的语句不会出错
  4. if let Some(_s) = s {
  5. println!("found a string");
  6. }
  7. println!("{:?}", s);

3.3.2 用..忽略值

3.3.2.1 示例1:用..忽略其他值

  1. struct Point {
  2. x: i32,
  3. y: i32,
  4. z: i32,
  5. }
  6. let origin = Point { x: 0, y: 0, z: 0 };
  7. match origin {
  8. // 忽略除x之外的其他字段
  9. Point { x, .. } => println!("x is {}", x),
  10. }

3.3.2.2 示例2:..会自动扩展匹配必需的值的数量

  1. fn main() {
  2. let numbers = (2, 4, 8, 16, 32);
  3. match numbers {
  4. // .. 会自动扩展匹配必需的值的数量:这里first,last分别匹配第一个和最后一个值
  5. (first, .., last) => {
  6. println!("Some numbers: {}, {}", first, last);
  7. },
  8. }
  9. }

3.3.2.3 示例3:使用..时不能有歧义

  1. fn main() {
  2. let numbers = (2, 4, 8, 16, 32);
  3. match numbers {
  4. // 这里有歧义,不能通过编译:前后的..应该分别匹配多少个值?
  5. (.., second, ..) => {
  6. println!("Some numbers: {}", second)
  7. },
  8. }
  9. }

3.3.3 用refref mut创建引用

  • 模式使用的变量将绑定一个值,外部变量的所有权将转移到模式变量中,这可能会导致问题
  • 此时使用解构引用(见上文)的方法是不行的,因为被匹配的值不是引用
  • 此时应该使用ref或者ref mut,表示取得被匹配值的引用
  1. let robot_name = Some(String::from("Bors"));
  2. match robot_name {
  3. // 1 robot_name携带的String变量的所有权被转移到模式变量name中
  4. // 后面使用robot_name的语句将出错:所有权已经转移,变量已经失效
  5. // 2 此时使用&name是不行的:被匹配的值不是引用,将出现类型不匹配的编译错误
  6. // 3 此时应该使用ref name,表示获取被匹配值的引用
  7. // 4 如果要修改被匹配值,则使用ref mut name,然后在对应的表达式中修改被匹配的值: Some(ref mut name) => *name = String::from("Another name"),
  8. Some(name) => println!("Found a name: {}", name),
  9. None => (),
  10. }
  11. println!("robot_name is: {:?}", robot_name);

3.3.4 匹配守卫(match guard)

  1. let num = Some(4);
  2. match num {
  3. Some(x) if x < 5 => println!("less than five: {}", x),
  4. Some(x) => println!("{}", x),
  5. None => (),
  6. }
  • 匹配守卫:match分支模式之后的额外if条件,守卫条件必须也被满足才可以选择相应的分支。
  • 注意次序:如果上面例子中第二个分支变成第一个,则新的第二个分支编程不可到达的分支,从而出现编译错误
  1. let num = Some(4);
  2. match num {
  3. Some(x) => println!("{}", x),
  4. // 这里会出编译错误:不可到达的分支
  5. Some(x) if x < 5 => println!("less than five: {}", x),
  6. None => (),
  7. }
  • 匹配守卫作用于分支中的整个模式。下面例子中的4 | 5 | 6 if y等价于(4 | 5 | 6) if y,而不是4 | 5 | (6 if y)
  1. let x = 4;
  2. let y = false;
  3. match x {
  4. 4 | 5 | 6 if y => println!("yes"),
  5. _ => println!("no"),
  6. }

3.3.5 @绑定

  • @绑定:绑定变量并且限定变量值必须满足的模式
  • 注意:@后面必须跟随模式,而不能跟随条件守卫
  1. enum Message {
  2. Hello { id: i32 },
  3. }
  4. let msg = Message::Hello { id: 5 };
  5. match msg {
  6. //也可以写成:Message::Hello { id: idval } if 3 <= idval && idval <= 7 => {
  7. Message::Hello { id: idval @ ..7} => {
  8. println!("Found an id in range: {}", idval)
  9. },
  10. // 没有绑定变量,仅限定字段值范围
  11. Message::Hello { id: 10...12 } => {
  12. println!("Found an id in another range")
  13. },
  14. Message::Hello { id } => {
  15. println!("Found some other id: {}", id)
  16. },
  17. }