match 表达式

语法
Match表达式 :
   match 被匹配项 {
      内部属性*
      Match分支组?
   }

被匹配项 :
   表达式不包括结构体表达式

Match分支组 :
   ( Match分支 => ( 无块表达式 , | 块表达式 ,? ) )*
   Match分支 => 表达式 ,?

Match分支 :
   外围属性* 模式 Match分支守卫?

Match分支守卫 :
   if 表达式

match 表达式 根据模式而进行分支。匹配所发生的确切形式取决于 模式match 表达式有一个 被匹配项 表达式,与模式进行比较。 被匹配表达式和模式必须类型相同。

match 的行为取决于被匹配表达式是一个 占位表达式还是值表达式。 如果被匹配表达式是一个 值表达式 ,它首先被求值到一个临时位置,然后将结果值按顺序与每个分支中的模式进行比较,直到找到匹配的模式。 第一个模式匹配的分支作为 match 的目标分支,任何由模式绑定的变量被分配为分支块局部变量,进入块中。

当被匹配表达式是一个 占位表达式 时,match 不会分配临时地址;但是,按值绑定时可能会从内存位置复制或移动。 如果可能的话,最好匹配占位表达式,因为这些匹配项的生命周期继承了占位表达式的生命周期,而不是被限制在 match 内部。

下面是一个 match 表达式的示例:

#![allow(unused)]
fn main() {
let x = 1;

match x {
    1 => println!("one"),
    2 => println!("two"),
    3 => println!("three"),
    4 => println!("four"),
    5 => println!("five"),
    _ => println!("something else"),
}
}

在模式中绑定的变量的作用域限定在 match 守卫及分支表达式中。绑定模式 (移动、复制或引用) 取决于模式。

多个匹配模式可以用 | 运算符连接起来。每个模式将按照从左到右的顺序进行测试,直到找到成功匹配的模式为止。

#![allow(unused)]
fn main() {
let x = 9;
let message = match x {
    0 | 1  => "not many",     // 如果 x 是 0 或 1,将 "not many" 赋值给 message
    2 ..= 9 => "a few",       // 如果 x 在 2 到 9 之间(包括 2 和 9),将 "a few" 赋值给 message
    _      => "lots"         // 否则将 "lots" 赋值给 message
};

assert_eq!(message, "a few");

// 演示模式匹配的顺序。
struct S(i32, i32);

match S(1, 2) {
    // 如果 S 的第一个元素是 1 或者第二个元素是 2,则将 z 绑定为 1,并且通过断言来检查 z 的值。
    S(z @ 1, _) | S(_, z @ 2) => assert_eq!(z, 1),  
    // 否则发生错误
    _ => panic!(),
}
}

注意: 2..=9区间模式 ,不是 区间表达式 。因此,在匹配分支中只能使用由区间模式支持的这些类型的区间。

每个 | 分隔的模式中的所有绑定都必须出现在分支的所有模式中。每个同名绑定必须类型相同,且绑定模式相同。

匹配守卫

match 分支可以接受 匹配守卫 来进一步细化匹配条件。 模式守卫出现在模式之后,由 if 关键字后面的 bool 类型表达式组成。

当模式成功匹配时,模式守卫表达式会被执行。如果表达式的结果为 true,则模式成功匹配。 否则,将测试下一个模式,包括同一分支中使用 | 运算符的其他匹配。

#![allow(unused)]
fn main() {
let maybe_digit = Some(0);
fn process_digit(i: i32) { }
fn process_other(i: i32) { }
let message = match maybe_digit {
    Some(x) if x < 10 => process_digit(x),
    Some(x) => process_other(x),
    None => panic!(),
};
}

注意:使用 | 操作符的多个匹配项可能导致模式守卫及其所产生的副作用执行多次。 例如:

#![allow(unused)]
fn main() {
use std::cell::Cell;
let i : Cell<i32> = Cell::new(0);
match 1 {
    1 | _ if { i.set(i.get() + 1); false } => {}
    _ => {}
}
assert_eq!(i.get(), 2);
}

模式守卫可以引用其所跟随的模式中绑定的变量。 在评估模式守卫之前,会对匹配变量的部分获取共享引用。 在评估模式守卫时,这个共享引用被用于访问该变量。 只有当模式守卫计算结果为 true 时,才会将值从待匹配项移动或复制到该变量中。 这允许在守卫中使用共享借用,如果守卫不匹配,则不会移动待匹配项。 此外,通过在评估守卫时保持共享引用,还可以防止在守卫内进行修改。

匹配分支上的属性

可以在匹配分支上使用外围属性。 在匹配分支上具有意义的外围属性只有 cfg代码分析检查属性内部属性 可以直接放置在匹配表达式的左括号后面,在与 块表达式上的属性 相同的表达式上下文中。