模式

语法
模式 :
      |? PatternNoTopAlt ( | PatternNoTopAlt )*

PatternNoTopAlt :
      PatternWithoutRange
   | RangePattern

PatternWithoutRange :
      LiteralPattern
   | IdentifierPattern
   | WildcardPattern
   | RestPattern
   | ReferencePattern
   | StructPattern
   | TupleStructPattern
   | TuplePattern
   | GroupedPattern
   | SlicePattern
   | PathPattern
   | MacroInvocation

模式用于将值与结构进行匹配,并可选择性地将变量绑定到这些结构内部的值。它们也用于变量声明以及函数和闭包的参数中。

以下示例中的模式执行了四项操作

  • 测试 person 是否具有填充了内容的 car 字段。
  • 测试 person 的 age 字段是否在 13 到 19 之间,并将其值绑定到 person_age 变量。
  • 将对 name 字段的引用绑定到变量 person_name
  • 忽略 person 的其余字段。剩余字段可以有任何值,并且不绑定到任何变量。
#![allow(unused)]
fn main() {
struct Car;
struct Computer;
struct Person {
    name: String,
    car: Option<Car>,
    computer: Option<Computer>,
    age: u8,
}
let person = Person {
    name: String::from("John"),
    car: Some(Car),
    computer: None,
    age: 15,
};
if let
    Person {
        car: Some(_),
        age: person_age @ 13..=19,
        name: ref person_name,
        ..
    } = person
{
    println!("{} has a car and is {} years old.", person_name, person_age);
}
}

模式用于

解构

模式可用于解构 结构体枚举元组。解构将值分解为其组成部分。使用的语法几乎与创建此类值时相同。在模式中,其 被检查者 表达式具有 structenumtuple 类型,占位符 (_) 代表单个数据字段,而通配符 .. 代表特定变体的所有剩余字段。当解构具有命名字段(但没有编号字段)的数据结构时,允许将 fieldname 写为 fieldname: fieldname 的简写。

#![allow(unused)]
fn main() {
enum Message {
    Quit,
    WriteString(String),
    Move { x: i32, y: i32 },
    ChangeColor(u8, u8, u8),
}
let message = Message::Quit;
match message {
    Message::Quit => println!("Quit"),
    Message::WriteString(write) => println!("{}", &write),
    Message::Move{ x, y: 0 } => println!("move {} horizontally", x),
    Message::Move{ .. } => println!("other move"),
    Message::ChangeColor { 0: red, 1: green, 2: _ } => {
        println!("color change, red: {}, green: {}", red, green);
    }
};
}

可反驳性

当模式有可能不被它正在匹配的值匹配时,该模式被称为是可反驳的。另一方面,不可反驳的模式总是与它们正在匹配的值匹配。示例

#![allow(unused)]
fn main() {
let (x, y) = (1, 2);               // "(x, y)" is an irrefutable pattern

if let (a, 3) = (1, 2) {           // "(a, 3)" is refutable, and will not match
    panic!("Shouldn't reach here");
} else if let (a, 4) = (3, 4) {    // "(a, 4)" is refutable, and will match
    println!("Matched ({}, 4)", a);
}
}

字面量模式

语法
LiteralPattern :
      true | false
   | CHAR_LITERAL
   | BYTE_LITERAL
   | STRING_LITERAL
   | RAW_STRING_LITERAL
   | BYTE_STRING_LITERAL
   | RAW_BYTE_STRING_LITERAL
   | C_STRING_LITERAL
   | RAW_C_STRING_LITERAL
   | -? INTEGER_LITERAL
   | -? FLOAT_LITERAL

字面量模式 完全匹配字面量创建的值。由于负数不是 字面量,字面量模式也接受字面量前的可选减号,其作用类似于取反运算符。

警告: C 字符串和原始 C 字符串字面量在字面量模式中被接受,但是 &CStr 没有实现结构相等性 (#[derive(Eq, PartialEq)]),因此任何对此 &CStrmatch 都将因类型错误而被拒绝。

字面量模式总是可反驳的。

示例

#![allow(unused)]
fn main() {
for i in -2..5 {
    match i {
        -1 => println!("It's minus one"),
        1 => println!("It's a one"),
        2|4 => println!("It's either a two or a four"),
        _ => println!("Matched none of the arms"),
    }
}
}

标识符模式

语法
IdentifierPattern :
      ref? mut? IDENTIFIER (@ PatternNoTopAlt ) ?

标识符模式将它们匹配的值绑定到 值命名空间 中的变量。标识符在模式中必须是唯一的。变量将遮蔽作用域中任何同名的变量。新绑定的 作用域 取决于模式的使用上下文(例如 let 绑定或 match 分支)。

仅由标识符组成的模式(可能带有 mut)匹配任何值并将其绑定到该标识符。这是变量声明以及函数和闭包参数中最常用的模式。

#![allow(unused)]
fn main() {
let mut variable = 10;
fn sum(x: i32, y: i32) -> i32 {
   x + y
}
}

要将模式的匹配值绑定到变量,请使用语法 variable @ subpattern。例如,以下代码将值 2 绑定到 e(而不是整个范围:这里的范围是范围子模式)。

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

match x {
    e @ 1 ..= 5 => println!("got a range element {}", e),
    _ => println!("anything"),
}
}

默认情况下,标识符模式将变量绑定到匹配值的副本或从匹配值移动,具体取决于匹配值是否实现了 Copy。这可以通过使用 ref 关键字绑定到引用来更改,或者使用 ref mut 绑定到可变引用。例如

#![allow(unused)]
fn main() {
let a = Some(10);
match a {
    None => (),
    Some(value) => (),
}

match a {
    None => (),
    Some(ref value) => (),
}
}

在第一个 match 表达式中,值被复制(或移动)。在第二个 match 中,对同一内存位置的引用被绑定到变量 value。之所以需要这种语法,是因为在解构子模式中,& 运算符不能应用于值的字段。例如,以下代码无效

#![allow(unused)]
fn main() {
struct Person {
   name: String,
   age: u8,
}
let value = Person { name: String::from("John"), age: 23 };
if let Person { name: &person_name, age: 18..=150 } = value { }
}

要使其有效,请编写以下代码

#![allow(unused)]
fn main() {
struct Person {
   name: String,
   age: u8,
}
let value = Person { name: String::from("John"), age: 23 };
if let Person { name: ref person_name, age: 18..=150 } = value { }
}

因此,ref 不是要匹配的东西。它的目的完全是将匹配的绑定设为引用,而不是可能复制或移动匹配的内容。

路径模式 优先于标识符模式。如果指定了 refref mut 且标识符遮蔽了常量,则会出错。

如果 @ 子模式是不可反驳的,或者未指定子模式,则标识符模式是不可反驳的。

绑定模式

为了提供更好的符合人体工程学的体验,模式以不同的绑定模式运行,以便更容易地绑定对值的引用。当引用值与非引用模式匹配时,它将自动被视为 refref mut 绑定。示例

#![allow(unused)]
fn main() {
let x: &Option<i32> = &Some(3);
if let Some(y) = x {
    // y was converted to `ref y` and its type is &i32
}
}

非引用模式 包括除绑定、通配符模式 (_)、const 模式(引用类型)和 引用模式 之外的所有模式。

如果绑定模式没有显式地使用 refref mutmut,那么它将使用默认绑定模式来确定变量的绑定方式。默认绑定模式从“移动”模式开始,该模式使用移动语义。当匹配模式时,编译器从模式的外部开始,向内工作。每次使用非引用模式匹配引用时,它都会自动解引用该值并更新默认绑定模式。引用会将默认绑定模式设置为 ref。可变引用会将模式设置为 ref mut,除非模式已经是 ref,在这种情况下它仍然是 ref。如果自动解引用的值仍然是引用,则会再次解引用并重复此过程。

当默认绑定模式为“移动”时,绑定模式可能仅显式指定 refref mut 绑定模式,或者使用 mut 指定可变性。例如,这些是不被接受的

#![allow(unused)]
fn main() {
let [mut x] = &[()]; //~ ERROR
let [ref x] = &[()]; //~ ERROR
let [ref mut x] = &mut [()]; //~ ERROR
}

版本差异:在 2024 版本之前,即使默认绑定模式不是“移动”,绑定也可以显式指定 refref mut 绑定模式,并且它们可以使用 mut 在此类绑定上指定可变性。在这些版本中,在绑定上指定 mut 会将绑定模式设置为“移动”,而与当前的默认绑定模式无关。

类似地,只有当默认绑定模式为“移动”时,才能出现引用模式。例如,这是不被接受的

#![allow(unused)]
fn main() {
let [&x] = &[&()]; //~ ERROR
}

版本差异:在 2024 版本之前,即使默认绑定模式不是“移动”,引用模式也可以出现,并且既具有与被检查者匹配的效果,又具有将默认绑定模式重置为“移动”的效果。

移动绑定和引用绑定可以在同一模式中混合使用。这样做会导致绑定到的对象的部分移动,并且该对象之后无法使用。这仅在类型无法复制时适用。

在下面的示例中,nameperson 中移出。尝试将 person 作为一个整体或 person.name 使用会导致错误,因为部分移动

示例

#![allow(unused)]
fn main() {
struct Person {
   name: String,
   age: u8,
}
let person = Person{ name: String::from("John"), age: 23 };
// `name` is moved from person and `age` referenced
let Person { name, ref age } = person;
}

通配符模式

语法
WildcardPattern :
   _

通配符模式(下划线符号)匹配任何值。它用于在值无关紧要时忽略它们。在其他模式内部,它匹配单个数据字段(与匹配剩余字段的 .. 相反)。与标识符模式不同,它不会复制、移动或借用它匹配的值。

示例

#![allow(unused)]
fn main() {
let x = 20;
let (a, _) = (10, x);   // the x is always matched by _
assert_eq!(a, 10);

// ignore a function/closure param
let real_part = |a: f64, _: f64| { a };

// ignore a field from a struct
struct RGBA {
   r: f32,
   g: f32,
   b: f32,
   a: f32,
}
let color = RGBA{r: 0.4, g: 0.1, b: 0.9, a: 0.5};
let RGBA{r: red, g: green, b: blue, a: _} = color;
assert_eq!(color.r, red);
assert_eq!(color.g, green);
assert_eq!(color.b, blue);

// accept any Some, with any value
let x = Some(10);
if let Some(_) = x {}
}

通配符模式总是不可反驳的。

Rest 模式

语法
RestPattern :
   ..

Rest 模式.. 标记)充当可变长度模式,它匹配零个或多个在之前和之后尚未匹配的元素。它只能在 元组元组结构体切片 模式中使用,并且只能作为这些模式中元素之一出现一次。它也允许在 标识符模式 中仅用于 切片模式

Rest 模式总是不可反驳的。

示例

#![allow(unused)]
fn main() {
let words = vec!["a", "b", "c"];
let slice = &words[..];
match slice {
    [] => println!("slice is empty"),
    [one] => println!("single element {}", one),
    [head, tail @ ..] => println!("head={} tail={:?}", head, tail),
}

match slice {
    // Ignore everything but the last element, which must be "!".
    [.., "!"] => println!("!!!"),

    // `start` is a slice of everything except the last element, which must be "z".
    [start @ .., "z"] => println!("starts with: {:?}", start),

    // `end` is a slice of everything but the first element, which must be "a".
    ["a", end @ ..] => println!("ends with: {:?}", end),

    // 'whole' is the entire slice and `last` is the final element
    whole @ [.., last] => println!("the last element of {:?} is {}", whole, last),

    rest => println!("{:?}", rest),
}

if let [.., penultimate, _] = slice {
    println!("next to last is {}", penultimate);
}

let tuple = (1, 2, 3, 4, 5);
// Rest patterns may also be used in tuple and tuple struct patterns.
match tuple {
    (1, .., y, z) => println!("y={} z={}", y, z),
    (.., 5) => println!("tail must be 5"),
    (..) => println!("matches everything else"),
}
}

范围模式

语法
RangePattern :
      RangeInclusivePattern
   | RangeFromPattern
   | RangeToInclusivePattern
   | ObsoleteRangePattern

RangeExclusivePattern :
      RangePatternBound .. RangePatternBound

RangeInclusivePattern :
      RangePatternBound ..= RangePatternBound

RangeFromPattern :
      RangePatternBound ..

RangeToInclusivePattern :
      ..= RangePatternBound

ObsoleteRangePattern :
   RangePatternBound ... RangePatternBound

RangePatternBound :
      CHAR_LITERAL
   | BYTE_LITERAL
   | -? INTEGER_LITERAL
   | -? FLOAT_LITERAL
   | PathExpression

范围模式 匹配在其边界定义的范围内的标量值。它们包括一个标记....=... 之一)以及一侧或两侧的边界。标记左侧的边界是下界。右侧的边界是上界

具有下界和上界的范围模式将匹配其边界之间(包括两个边界)的所有值。它写作下界,后跟 .. 表示末端不包含,或 ..= 表示末端包含,然后是其上界。范围模式的类型是其上界和下界的类型统一。

例如,模式 'm'..='p' 将仅匹配值 'm''n''o''p'。类似地,'m'..'p' 将仅匹配 'm''n''o',特别地,包括 'p'

下界不能大于上界。也就是说,在 a..=b 中,a ≤ b 必须成立。例如,范围模式 10..=0 是错误的。

仅具有下界的范围模式将匹配任何大于或等于下界的值。它写作下界,后跟 ..,并且具有与其下界相同的类型。例如,1.. 将匹配 1、9 或 9001 或 9007199254740991(如果它具有适当的大小),但不匹配 0,也不匹配有符号整数的负数。

仅具有上界的范围模式匹配任何小于或等于上界的值。它写作 ..=,后跟其上界,并且具有与其上界相同的类型。例如,..=10 将匹配 10、1、0,对于有符号整数类型,匹配所有负值。

仅具有一个边界的范围模式不能用作 切片模式 中子模式的顶层模式。

边界写作以下形式之一

  • 字符、字节、整数或浮点字面量。
  • - 后跟整数或浮点字面量。
  • 路径

如果边界写成路径,则在宏解析后,该路径必须解析为 char 类型、整数类型或浮点类型的常量项。

边界的类型和值取决于它的书写方式。如果边界是 路径,则模式具有路径解析到的 常量 的类型和值。对于浮点范围模式,常量不能是 NaN。如果是字面量,则它具有相应 字面量表达式 的类型和值。如果是字面量前导有 -,则它具有与相应 字面量表达式 相同的类型,以及 取反 相应字面量表达式的值的值。

示例

#![allow(unused)]
fn main() {
let c = 'f';
let valid_variable = match c {
    'a'..='z' => true,
    'A'..='Z' => true,
    'α'..='ω' => true,
    _ => false,
};

let ph = 10;
println!("{}", match ph {
    0..7 => "acid",
    7 => "neutral",
    8..=14 => "base",
    _ => unreachable!(),
});

let uint: u32 = 5;
match uint {
    0 => "zero!",
    1.. => "positive number!",
};

// using paths to constants:
const TROPOSPHERE_MIN : u8 = 6;
const TROPOSPHERE_MAX : u8 = 20;

const STRATOSPHERE_MIN : u8 = TROPOSPHERE_MAX + 1;
const STRATOSPHERE_MAX : u8 = 50;

const MESOSPHERE_MIN : u8 = STRATOSPHERE_MAX + 1;
const MESOSPHERE_MAX : u8 = 85;

let altitude = 70;

println!("{}", match altitude {
    TROPOSPHERE_MIN..=TROPOSPHERE_MAX => "troposphere",
    STRATOSPHERE_MIN..=STRATOSPHERE_MAX => "stratosphere",
    MESOSPHERE_MIN..=MESOSPHERE_MAX => "mesosphere",
    _ => "outer space, maybe",
});

pub mod binary {
    pub const MEGA : u64 = 1024*1024;
    pub const GIGA : u64 = 1024*1024*1024;
}
let n_items = 20_832_425;
let bytes_per_item = 12;
if let size @ binary::MEGA..=binary::GIGA = n_items * bytes_per_item {
    println!("It fits and occupies {} bytes", size);
}

trait MaxValue {
    const MAX: u64;
}
impl MaxValue for u8 {
    const MAX: u64 = (1 << 8) - 1;
}
impl MaxValue for u16 {
    const MAX: u64 = (1 << 16) - 1;
}
impl MaxValue for u32 {
    const MAX: u64 = (1 << 32) - 1;
}
// using qualified paths:
println!("{}", match 0xfacade {
    0 ..= <u8 as MaxValue>::MAX => "fits in a u8",
    0 ..= <u16 as MaxValue>::MAX => "fits in a u16",
    0 ..= <u32 as MaxValue>::MAX => "fits in a u32",
    _ => "too big",
});
}

当固定宽度整数和 char 类型的范围模式跨越类型的整个可能值集时,它们是不可反驳的。例如,0u8..=255u8 是不可反驳的。整数类型的值范围是从其最小值到最大值的闭合范围。char 类型的值范围正是那些包含所有 Unicode 标量值的范围:'\u{0000}'..='\u{D7FF}''\u{E000}'..='\u{10FFFF}'

版本差异:在 2021 版本之前,具有下界和上界的范围模式也可以使用 ... 代替 ..= 来编写,含义相同。

引用模式

语法
ReferencePattern :
   (&|&&) mut? PatternWithoutRange

引用模式解引用正在匹配的指针,从而借用它们。

例如,以下两个对 x: &i32 的匹配是等效的

#![allow(unused)]
fn main() {
let int_reference = &3;

let a = match *int_reference { 0 => "zero", _ => "some" };
let b = match int_reference { &0 => "zero", _ => "some" };

assert_eq!(a, b);
}

引用模式的语法产生式必须匹配标记 && 才能匹配对引用的引用,因为它本身就是一个标记,而不是两个 & 标记。

添加 mut 关键字会解引用可变引用。可变性必须与引用的可变性匹配。

引用模式总是不可反驳的。

结构体模式

语法
StructPattern :
   PathInExpression {
      StructPatternElements ?
   }

StructPatternElements :
      StructPatternFields (, | , StructPatternEtCetera)?
   | StructPatternEtCetera

StructPatternFields :
   StructPatternField (, StructPatternField) *

StructPatternField :
   OuterAttribute *
   (
         TUPLE_INDEX : Pattern
      | IDENTIFIER : Pattern
      | ref? mut? IDENTIFIER
   )

StructPatternEtCetera :
   OuterAttribute *
   ..

结构体模式匹配符合其子模式定义的所有标准的结构体、枚举和联合体值。它们也用于解构结构体、枚举或联合体值。

在结构体模式中,字段通过名称、索引(在元组结构体的情况下)引用,或者通过使用 .. 忽略。

#![allow(unused)]
fn main() {
struct Point {
    x: u32,
    y: u32,
}
let s = Point {x: 1, y: 1};

match s {
    Point {x: 10, y: 20} => (),
    Point {y: 10, x: 20} => (),    // order doesn't matter
    Point {x: 10, ..} => (),
    Point {..} => (),
}

struct PointTuple (
    u32,
    u32,
);
let t = PointTuple(1, 2);

match t {
    PointTuple {0: 10, 1: 20} => (),
    PointTuple {1: 10, 0: 20} => (),   // order doesn't matter
    PointTuple {0: 10, ..} => (),
    PointTuple {..} => (),
}

enum Message {
    Quit,
    Move { x: i32, y: i32 },
}
let m = Message::Quit;

match m {
    Message::Quit => (),
    Message::Move {x: 10, y: 20} => (),
    Message::Move {..} => (),
}
}

如果未使用 ..,则用于匹配结构体的结构体模式需要指定所有字段

#![allow(unused)]
fn main() {
struct Struct {
   a: i32,
   b: char,
   c: bool,
}
let mut struct_value = Struct{a: 10, b: 'X', c: false};

match struct_value {
    Struct{a: 10, b: 'X', c: false} => (),
    Struct{a: 10, b: 'X', ref c} => (),
    Struct{a: 10, b: 'X', ref mut c} => (),
    Struct{a: 10, b: 'X', c: _} => (),
    Struct{a: _, b: _, c: _} => (),
}
}

用于匹配联合体的结构体模式必须恰好指定一个字段(参见 联合体的模式匹配)。

ref 和/或 mut IDENTIFIER 语法匹配任何值,并将其绑定到与给定字段同名的变量。

#![allow(unused)]
fn main() {
struct Struct {
   a: i32,
   b: char,
   c: bool,
}
let struct_value = Struct{a: 10, b: 'X', c: false};

let Struct{a: x, b: y, c: z} = struct_value;          // destructure all fields
}

如果 PathInExpression 解析为具有多个变体的枚举的构造函数,或者其子模式之一是可反驳的,则结构体模式是可反驳的。

元组结构体模式

语法
TupleStructPattern :
   PathInExpression ( TupleStructItems? )

TupleStructItems :
   Pattern ( , Pattern )* ,?

元组结构体模式匹配符合其子模式定义的所有标准的元组结构体和枚举值。它们也用于 解构 元组结构体或枚举值。

如果 PathInExpression 解析为具有多个变体的枚举的构造函数,或者其子模式之一是可反驳的,则元组结构体模式是可反驳的。

元组模式

语法
TuplePattern :
   ( TuplePatternItems? )

TuplePatternItems :
      Pattern ,
   | RestPattern
   | Pattern (, Pattern)+ ,?

元组模式匹配符合其子模式定义的所有标准的元组值。它们也用于 解构 元组。

具有单个 RestPattern 的形式 (..) 是一种特殊形式,不需要逗号,并且匹配任何大小的元组。

当其子模式之一是可反驳的时,元组模式是可反驳的。

使用元组模式的示例

#![allow(unused)]
fn main() {
let pair = (10, "ten");
let (a, b) = pair;

assert_eq!(a, 10);
assert_eq!(b, "ten");
}

分组模式

语法
GroupedPattern :
   ( Pattern )

将模式括在括号中可以用于显式控制复合模式的优先级。例如,范围模式旁边的引用模式(例如 &0..=5)是模棱两可的,并且是不允许的,但是可以使用括号来表达。

#![allow(unused)]
fn main() {
let int_reference = &3;
match int_reference {
    &(0..=5) => (),
    _ => (),
}
}

切片模式

语法
SlicePattern :
   [ SlicePatternItems? ]

SlicePatternItems :
   Pattern (, Pattern)* ,?

切片模式可以匹配固定大小的数组和动态大小的切片。

#![allow(unused)]
fn main() {
// Fixed size
let arr = [1, 2, 3];
match arr {
    [1, _, _] => "starts with one",
    [a, b, c] => "starts with something else",
};
}
#![allow(unused)]
fn main() {
// Dynamic size
let v = vec![1, 2, 3];
match v[..] {
    [a, b] => { /* this arm will not apply because the length doesn't match */ }
    [a, b, c] => { /* this arm will apply */ }
    _ => { /* this wildcard is required, since the length is not known statically */ }
};
}

当匹配数组时,只要每个元素都是不可反驳的,切片模式就是不可反驳的。当匹配切片时,它仅在具有单个 .. Rest 模式标识符模式(以 .. Rest 模式作为子模式)的形式中是不可反驳的。

在切片中,没有上下界的范围模式必须用括号括起来,如 (a..),以明确表示它旨在匹配单个切片元素。具有上下界的范围模式(如 a..=b)不需要用括号括起来。

路径模式

语法
PathPattern :
      PathExpression

路径模式 是指代常量值或没有字段的结构体或枚举变体的模式。

非限定路径模式可以指代

  • 枚举变体
  • 结构体
  • 常量
  • 关联常量

限定路径模式只能指代关联常量。

当路径模式指代结构体或枚举只有一个变体时的枚举变体,或者其类型不可反驳的常量时,路径模式是不可反驳的。当它们指代可反驳的常量或具有多个变体的枚举的枚举变体时,它们是可反驳的。

常量模式

当类型为 T 的常量 C 用作模式时,我们首先检查 T: PartialEq。此外,我们要求 C 的值具有(递归)结构相等性,其递归定义如下

  • 整数以及 strboolchar 值始终具有结构相等性。
  • 如果元组、数组和切片的所有字段/元素都具有结构相等性,则它们具有结构相等性。(特别是,()[] 始终具有结构相等性。)
  • 如果引用指向的值具有结构相等性,则引用具有结构相等性。
  • 如果 structenum 类型的 PartialEq 实例是通过 #[derive(PartialEq)] 派生的,并且所有字段(对于枚举:活动变体的字段)都具有结构相等性,则该类型的值具有结构相等性。
  • 如果原始指针定义为常量整数(然后强制转换/转置),则它具有结构相等性。
  • 如果浮点值不是 NaN,则它具有结构相等性。
  • 其他任何东西都不具有结构相等性。

特别是,C 的值必须在模式构建时(即在单态化之前)已知。这意味着涉及泛型参数的关联常量不能用作模式。

在确保满足所有条件后,常量值将转换为模式,并且现在的行为完全如同直接编写该模式一样。特别是,它完全参与穷尽性检查。(对于原始指针,常量是编写此类模式的唯一方法。对于这些类型,仅 _ 被认为是穷尽的。)

或模式

或模式 是匹配两个或多个子模式之一的模式(例如 A | B | C)。它们可以任意嵌套。在语法上,或模式允许在其他模式允许的任何位置(由 Pattern 产生式表示),但 let 绑定以及函数和闭包参数(由 PatternNoTopAlt 产生式表示)除外。

静态语义

  1. 给定深度为 d 的模式 p | q,对于某些任意模式 pq,如果出现以下情况,则该模式被认为是形式错误的

    • p 推断的类型与为 q 推断的类型不统一,或者
    • pq 中未引入相同的绑定集,或者
    • pq 中任何两个同名绑定的类型在类型或绑定模式方面不统一。

    类型的统一在所有上述情况下都是精确的,并且不应用隐式 类型强制转换

  2. 当类型检查表达式 match e_s { a_1 => e_1, ... a_n => e_n } 时,对于每个包含 p_i | q_i 形式模式的匹配分支 a_i,如果模式 p_i | q_i 在深度 d 处是形式错误的,其中它存在于深度 d 处的 e_s 片段,则表达式片段的类型与 p_i | q_i 不统一。

  3. 关于穷尽性检查,模式 p | q 被认为涵盖 p 以及 q。对于某个构造函数 c(x, ..),分配律适用,使得 c(p | q, ..rest) 涵盖与 c(p, ..rest) | c(q, ..rest) 相同的值集。可以递归地应用此规则,直到除了顶层存在的 p | q 形式的嵌套模式之外,没有其他嵌套模式为止。

    请注意,通过“构造函数”,我们不是指元组结构体模式,而是指任何乘积类型的模式。这包括枚举变体、元组结构体、具有命名字段的结构体、数组、元组和切片。

动态语义

  1. 在深度 d 处将受检表达式 e_s 与模式 c(p | q, ..rest) 进行匹配的动态语义,其中 c 是某个构造函数,pq 是任意模式,rest 可选地是 c 中任何剩余的潜在因子,其定义与 c(p, ..rest) | c(q, ..rest) 的动态语义相同。

与其他无分隔符模式的优先级

如本章其他部分所示,有几种类型的模式在语法上是无分隔符的,包括标识符模式、引用模式和或模式。或模式始终具有最低优先级。这使我们可以为可能的未来类型注释功能保留语法空间,并减少歧义。例如,x @ A(..) | B(..) 将导致错误,即 x 未在所有模式中绑定。&A(x) | B(x) 将导致不同子模式中的 x 之间存在类型不匹配。