模式

语法
Pattern :
      |? PatternNoTopAlt ( | PatternNoTopAlt )*

PatternNoTopAlt :
      PatternWithoutRange
   | RangePattern

PatternWithoutRange :
      PatternWithoutRange
   | 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);
}
}

字面量模式

语法
PatternWithoutRange :
      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)]),因此对 &CStr 的任何此类 match 都将被拒绝并报类型错误。

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

示例

#![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 版本之前,即使默认绑定模式不是“移动”,引用模式也可以出现,并且既具有与被匹配值匹配的效果,又导致默认绑定模式重置为“移动”。

移动绑定和引用绑定可以混合在同一个模式中。这样做会导致对象的局部移动,之后该对象无法再使用。这仅适用于类型不可复制(Copy)的情况。

在下面的示例中,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 {}
}

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

剩余模式

语法
RestPattern :
   ..

剩余模式.. 标记)充当可变长度模式,匹配之前和之后尚未匹配的零个或多个元素。

它只能用于元组元组结构体切片模式,并且在这些模式中只能作为其中一个元素出现一次。它也仅在切片模式标识符模式中被允许使用。

剩余模式总是不可反驳的。

示例

#![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 :
      RangeExclusivePattern
   | RangeInclusivePattern
   | RangeFromPattern
   | RangeToExclusivePattern
   | RangeToInclusivePattern
   | ObsoleteRangePattern1

RangeExclusivePattern :
      RangeExclusivePattern

RangePatternBound .. RangePatternBound :
      RangeInclusivePattern

RangePatternBound ..= RangePatternBound :
      RangeFromPattern

RangePatternBound .. :
      RangeToExclusivePattern

.. RangePatternBound :
      RangeToInclusivePattern

..= RangePatternBound :
   ObsoleteRangePattern

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

1

   | PathExpression

[patterns.range.intro]

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

[patterns.range.exclusive]

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

[patterns.range.inclusive]

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

[patterns.range.from]

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

[patterns.range.to-exclusive]

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

[patterns.range.to-inclusive]

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

[patterns.range.constraint-less-than]

[patterns.range.bound]

  • 边界可以写成以下形式之一
  • 字符、字节、整数或浮点字面量。
  • - 后跟整数或浮点字面量。

[patterns.range.constraint-bound-path]

[patterns.range.type]

[patterns.range.path-value]

如果边界是路径,则边界匹配该路径解析到的常量的类型并具有其值。

[patterns.range.literal-value]

[patterns.range.negation]

如果边界是字面量前有 -,则边界匹配相应字面量表达式的类型,并具有对相应字面量表达式的值进行否定后的值。

[patterns.range.float-restriction]

示例

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

[patterns.range.refutable]

[patterns.range.refutable-integer]

[patterns.range.refutable-char]

[patterns.range.constraint-slice]

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

[patterns.range.edition2021]

[patterns.ref]

语法
[patterns.ref.syntax] :
ReferencePattern

[patterns.ref.intro]

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

#![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);
}

[patterns.ref.ref-ref]

[patterns.ref.mut]

[patterns.ref.refutable]

[patterns.struct]

语法
[patterns.struct.syntax] :
   StructPattern
      PathInExpression {
   }

StructPatternElements ? :
      }
StructPatternElements

StructPatternFields (, | , StructPatternEtCetera)? :
      | StructPatternEtCetera

StructPatternFields :
   StructPatternField (, StructPatternField) *
StructPatternField
         OuterAttribute *
   (
TUPLE_INDEX : Pattern
      | IDENTIFIER : Pattern

      | ref? mut? IDENTIFIER :
   ..

StructPatternEtCetera

[patterns.struct.intro]

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

[patterns.struct.ignore-rest]

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

[patterns.struct.constraint-struct]

[patterns.struct.constraint-union]

#![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
}

[patterns.struct.binding-shorthand]

[patterns.struct.refutable]

语法
[patterns.tuple-struct] :
   元组结构体模式

[patterns.tuple-struct.syntax] :
   TupleStructPattern

PathInExpression ( TupleStructItems? )

TupleStructItems

Pattern ( , Pattern )* ,?

[patterns.tuple-struct.intro]

[patterns.tuple-struct.refutable]

语法
[patterns.tuple] :
   元组模式

[patterns.tuple.syntax] :
      TuplePattern
   | RestPattern
( TuplePatternItems? )

Pattern ,

[patterns.tuple.intro]

[patterns.tuple.rest-syntax]

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

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

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

当其某个子模式是可反驳的时,元组模式是可反驳的。

语法
[patterns.paren] :
   分组模式

GroupedPattern

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

[patterns.paren.intro]

语法
[patterns.slice] :
   切片模式

[patterns.slice.syntax] :
   SlicePattern

SlicePatternItems

#![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 */ }
};
}
Pattern (, Pattern)* ,?

[patterns.slice.intro]

[patterns.slice.refutable-array]

[patterns.slice.refutable-slice]

当匹配切片时,只有当形式为单个 .. 剩余模式 或带有 .. 剩余模式作为子模式的标识符模式时,它才是不可反驳的。

[patterns.slice.restriction]

语法
[patterns.path] :
      路径模式

PathPattern

[patterns.path.intro]

  • 路径模式是指向常量值或没有字段的结构体或枚举变体的模式。
  • [patterns.path.unqualified]
  • 不限定的路径模式可以引用
  • 枚举变体

常量

[patterns.path.qualified]

[patterns.path.refutable]

[patterns.const]

[patterns.const.partial-eq]

  • [patterns.const.structural-equality]
  • [patterns.const.primitive]
  • [patterns.const.builtin-aggregate]
  • [patterns.const.ref]
  • [patterns.const.aggregate]
  • [patterns.const.pointer]
  • [patterns.const.float]

[patterns.const.exhaustive]

[patterns.const.generic]

[patterns.const.translation]

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

或模式

  1. [patterns.constraints]

    • 静态语义
    • [patterns.constraints.pattern]
    • 给定任意模式 pq 在某个深度的模式 p | q,如果出现以下情况,则认为该模式格式不正确

    推断出 p 的类型与推断出 q 的类型不统一,或

  1. pq 中,具有相同名称的任意两个绑定在类型或绑定模式方面不统一。
  1. [patterns.constraints.match-type-check]

    当类型检查表达式 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 格式不正确。

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

  1. [patterns.behavior]

[patterns.behavior.nested-or-patterns]

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