Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

模式

Syntax
Pattern|? PatternNoTopAlt ( | PatternNoTopAlt )*

PatternNoTopAlt
      PatternWithoutModernRange
    | ModernRangePattern

PatternWithoutModernRange
      LiteralPattern
    | IdentifierPattern
    | WildcardPattern
    | RestPattern
    | ReferencePattern
    | StructPattern
    | TupleStructPattern
    | TuplePattern
    | GroupedPattern
    | SlicePattern
    | PathPattern
    | MacroInvocation
    | ObsoleteRangePattern1

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

以下示例中的模式做了四件事:

  • 测试 personcar 字段是否填充了某些内容。
  • 测试人的 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);
}
}

模式用于:

  • 函数和[闭包][closure]参数

解构

模式可用于*[解构][destructure]*结构体枚举元组。解构将值分解为其组成部分。使用的语法与创建此类值时几乎相同。

审查者表达式具有 structenumtuple 类型的模式中,[通配符模式][wildcard pattern](_)代表单个数据字段,而[等等][et cetera]或[休息模式][rest pattern](..)代表特定变体的所有剩余字段。

当解构具有命名(但不是编号)字段的数据结构时,允许编写 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);
}
}

字面量模式

Syntax
LiteralPattern-? LiteralExpression

_字面量模式_精确匹配与字面量创建的值相同的值。由于负数不是字面量,模式中的字面量可以在前面加上可选的减号,其作用类似于取反运算符。

Warning

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

标识符模式

Syntax
IdentifierPatternref? 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 中,对同一内存位置的引用被绑定到变量值。需要此语法是因为在解构子模式中,& 运算符不能应用于值的字段。例如,以下代码无效:

#![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 不是正在匹配的内容。其目的完全是为了使匹配的绑定成为引用,而不是潜在地复制或移动被匹配的内容。

[路径模式][Path patterns]优先于标识符模式。

Note

当模式是单段标识符时,语法上存在歧义,不知道它是 IdentifierPattern 还是 PathPattern。此歧义只能在名称解析之后解决。

#![allow(unused)]
fn main() {
const EXPECTED_VALUE: u8 = 42;
//    ^^^^^^^^^^^^^^ That this constant is in scope affects how the
//                   patterns below are treated.

fn check_value(x: u8) -> Result<u8, u8> {
    match x {
        EXPECTED_VALUE => Ok(x),
    //  ^^^^^^^^^^^^^^ Parsed as a `PathPattern` that resolves to
    //                 the constant `42`.
        other_value => Err(x),
    //  ^^^^^^^^^^^ Parsed as an `IdentifierPattern`.
    }
}

// If `EXPECTED_VALUE` were treated as an `IdentifierPattern` above,
// that pattern would always match, making the function always return
// `Ok(_) regardless of the input.
assert_eq!(check_value(42), Ok(42));
assert_eq!(check_value(43), Err(43));
}

如果指定了 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
}
}

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

如果绑定模式没有显式具有 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 Edition differences

在 2024 版本之前,即使默认绑定模式不是“移动“,绑定也可以显式指定 refref mut 绑定模式,并且可以使用 mut 在此类绑定上指定可变性。在这些版本中,在绑定上指定 mut 会将绑定模式设置为“移动“,而不管当前的默认绑定模式如何。

类似地,引用模式只能在默认绑定模式是“移动“时出现。例如,这不被接受:

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

2024 Edition differences

在 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;
}

通配符模式

Syntax
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 {}
}

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

休息模式

Syntax
RestPattern..

休息模式.. 词法单元)充当可变长度模式,匹配之前和之后尚未匹配的零个或多个元素。

它只能在元组、[元组结构体][tuple struct]和切片模式中使用,并且只能作为这些模式中的元素之一出现一次。它也仅在[切片模式][slice patterns]的[标识符模式][identifier pattern]中允许。

休息模式总是不可反驳的。

示例:

#![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);
// The rest pattern 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"),
}
}

范围模式

范围模式匹配其边界定义的范围内的标量值。它们由符号....=)和一侧或两侧的边界组成。

符号左侧的边界称为下界。右侧的边界称为上界

排他范围模式匹配从下界到上界(不包括上界)的所有值。它写为下界,后跟 ..,后跟上界。

例如,模式 'm'..'p' 将只匹配 'm''n''o',特别包括 'p'

包含范围模式匹配从下界到上界(包括上界)的所有值。它写为下界,后跟 ..=,后跟上界。

例如,模式 'm'..='p' 将只匹配值 'm''n''o''p'

从范围模式匹配大于或等于下界的所有值。它写为下界后跟 ..

例如,1.. 将匹配任何大于或等于 1 的整数,如 1、9 或 9001,或 9007199254740991(如果大小合适),但不匹配 0,对于有符号整数不匹配负数。

到排他范围模式匹配小于上界的所有值。它写为 .. 后跟上界。

例如,..10 将匹配任何小于 10 的整数,如 9、1、0,对于有符号整数类型,匹配所有负值。

到包含范围模式匹配小于或等于上界的所有值。它写为 ..= 后跟上界。

例如,..=10 将匹配任何小于或等于 10 的整数,如 10、1、0,对于有符号整数类型,匹配所有负值。

范围模式必须非空;它必须跨越其类型可能值集中至少一个值。换句话说:

  • a..=b 中,必须满足 a ≤ b。例如,范围模式 10..=0 是错误的,但 10..=10 是允许的。
  • a..b 中,必须满足 a < b。例如,范围模式 10..010..10 是错误的。
  • ..b 中,b 不能是其类型的最小值。例如,范围模式 ..-128i8..f64::NEG_INFINITY 是错误的。

边界写为以下之一:

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

Note

我们在语法上接受的比这更多作为 RangePatternBound。我们稍后在语义上拒绝其他内容。

如果边界写为路径,在宏解析之后,路径必须解析为类型为 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}'

RangeFromPattern 不能用作[切片模式][slice patterns]中子模式的顶级模式。例如,模式 [1.., _] 不是有效模式。

2021 Edition differences

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

引用模式

Syntax
ReferencePattern → ( & | && ) mut? PatternWithoutModernRange

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

例如,这两个对 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 关键字解引用可变引用。可变性必须与引用的可变性匹配。

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

结构体模式

Syntax
StructPattern
    PathInExpression {
        StructPatternElements?
    }

StructPatternElements
      StructPatternFields ( , | , StructPatternEtCetera )?
    | StructPatternEtCetera

StructPatternFields
    StructPatternField ( , StructPatternField )*

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

StructPatternEtCetera..

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

在结构体模式上,字段按名称、索引(对于元组结构体)引用,或通过使用 .. 忽略:

#![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: _} => (),
}
}

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

IDENTIFIER 语法匹配任何值并将其绑定到与给定字段同名的变量。它是 fieldname: fieldname 的简写。refmut 限定符可以包含在内,行为如 patterns.ident.ref 中所述。

#![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, b, c } = struct_value;
}

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

结构体模式匹配构造函数从类型命名空间中的 PathInExpression 解析的结构体、联合体或枚举变体。有关更多详细信息,请参阅 patterns.tuple-struct.namespace

元组结构体模式

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

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

元组结构体模式匹配构造函数从值命名空间中的 PathInExpression 解析的元组结构体或类元组枚举变体

Note

相反,元组结构体或类元组枚举变体的结构体模式,例如 S { 0: _ },匹配构造函数在类型命名空间中解析的元组结构体或变体。

enum E1 { V(u16) }
enum E2 { V(u32) }

// Import `E1::V` from the type namespace only.
mod _0 {
    const V: () = (); // For namespace masking.
    pub(super) use super::E1::*;
}
use _0::*;

// Import `E2::V` from the value namespace only.
mod _1 {
    struct V {} // For namespace masking.
    pub(super) use super::E2::*;
}
use _1::*;

fn f() {
    // This struct pattern matches against the tuple-like
    // enum variant whose constructor was found in the type
    // namespace.
    let V { 0: ..=u16::MAX } = (loop {}) else { loop {} };
    // This tuple struct pattern matches against the tuple-like
    // enum variant whose constructor was found in the value
    // namespace.
    let V(..=u32::MAX) = (loop {}) else { loop {} };
}
// Required due to the odd behavior of `super` within functions.
fn main() {}

Lang 团队已经做出了某些决定,例如在 PR #138458 中,这引发了关于在模式中以这种方式使用值命名空间的可取性的疑问,如 PR #140593 中所述。在代码中不故意依赖此细微差别可能是谨慎的。

元组模式

Syntax
TuplePattern( TuplePatternItems? )

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

元组模式匹配满足其子模式定义的所有标准的元组值。它们也用于[解构][destructure]元组。

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

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

使用元组模式的示例:

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

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

分组模式

Syntax
GroupedPattern( Pattern )

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

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

切片模式

Syntax
SlicePattern[ SlicePatternItems? ]

SlicePatternItemsPattern ( , 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 pattern]或带有 .. 休息模式作为子模式的[标识符模式][identifier pattern]的形式才是不可反驳的。

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

路径模式

Syntax
PathPatternPathExpression

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

非限定路径模式可以引用:

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

限定路径模式只能引用关联常量。

当路径模式引用结构体或枚举变体(枚举只有一个变体)或类型为不可反驳的常量时,它们是不可反驳的。当它们引用可反驳的常量或具有多个变体的枚举的枚举变体时,它们是可反驳的。

常量模式

当类型为 T 的常量 C 用作模式时,我们首先检查 T: PartialEq

此外,我们要求 C 的值具有(递归)结构相等性,其递归定义如下:

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

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

C 的值不得包含对可变静态(static mut 项或内部可变 static 项)或 extern 静态的任何引用。

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

或模式

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

静态语义

  1. 给定某个深度处的模式 p | q,对于某些任意模式 pq,如果满足以下条件,则该模式被认为是格式错误的:

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

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

  1. 当对表达式 match e_s { a_1 => e_1, ... a_n => e_n } 进行类型检查时,对于包含形式为 p_i | q_i 的模式的每个 match 分支 a_i,如果在其存在的深度 d 处,e_s 的片段类型与 p_i | q_i 不统一,则模式 p_i | q_i 被认为是格式错误的。
  1. 关于穷举性检查,模式 p | q 被认为覆盖 p 以及 q。对于某个构造函数 c(x, ..),分配律适用,使得 c(p | q, ..rest) 覆盖与 c(p, ..rest) | c(q, ..rest) 相同的值集。这可以递归应用,直到没有更多形式为 p | q 的嵌套模式(除了那些存在于顶级的模式)。

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

动态语义

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

与其他非分隔模式的优先级

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


  1. ObsoleteRangePattern 语法在 2021 版本及以后在语义上无效。