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
      PatternWithoutRange
    | RangePattern

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

Паттерны используются для сопоставления значений со структурами и, опционально, привязки переменных к значениям внутри этих структур. Они также используются в объявлениях переменных и параметрах функций и замыканий.

Паттерн в следующем примере делает четыре вещи:

  • Проверяет, заполнено ли поле 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);
}
}

Паттерны используются в:

  • Выражениях match
  • Выражениях for

Деструктуризация

Паттерны могут использоваться для деструктуризации структур, перечислений и кортежей. Деструктуризация разбивает значение на составляющие его части. Используемый синтаксис почти такой же, как при создании таких значений.

В паттерне, выражение-образец которого имеет тип struct, enum или tuple, подстановочный паттерн (_) заменяет одно поле данных, тогда как и так далее или остаточный паттерн (..) заменяет все оставшиеся поля определённого варианта.

При деструктуризации структуры данных с именованными (но не нумерованными) полями разрешается писать 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)" - неопровержимый паттерн

if let (a, 3) = (1, 2) {           // "(a, 3)" опровержим и не совпадёт
    panic!("Shouldn't reach here");
} else if let (a, 4) = (3, 4) {    // "(a, 4)" опровержим и совпадёт
    println!("Matched ({}, 4)", a);
}
}

Литеральные паттерны

Syntax
LiteralPattern-? LiteralExpression

Литеральные паттерны сопоставляются точно с тем же значением, что создаётся литералом. Поскольку отрицательные числа не являются литералами, литералы в паттернах могут иметь необязательный знак минус, который действует как оператор отрицания.

Warning

C-строковые и сырые C-строковые литералы принимаются в литеральных паттернах, но &CStr не реализует структурное равенство (#[derive(Eq, PartialEq)]) и поэтому любой такой match на &CStr будет отклонён с ошибкой типа.

Литеральные паттерны всегда опровержимы.

Примеры:

#![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 ссылка на ту же область памяти привязывается к переменной 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 не является чем-то, с чем сопоставляется. Его цель исключительно в том, чтобы сделать привязку ссылкой, вместо потенциального копирования или перемещения того, что было сопоставлено.

Путевые паттерны имеют приоритет над идентификаторными паттернами.

Note

Когда паттерн является идентификатором с одним сегментом, грамматика неоднозначна в том, означает ли он IdentifierPattern или PathPattern. Эта неоднозначность может быть разрешена только после разрешения имён.

#![allow(unused)]
fn main() {
const EXPECTED_VALUE: u8 = 42;
//    ^^^^^^^^^^^^^^ То, что эта константа в области видимости, влияет на то, как
//                   обрабатываются паттерны ниже.

fn check_value(x: u8) -> Result<u8, u8> {
    match x {
        EXPECTED_VALUE => Ok(x),
    //  ^^^^^^^^^^^^^^ Разбирается как `PathPattern`, который разрешается в
    //                 константу `42`.
        other_value => Err(x),
    //  ^^^^^^^^^^^ Разбирается как `IdentifierPattern`.
    }
}

// Если бы `EXPECTED_VALUE` рассматривался как `IdentifierPattern` выше,
// этот паттерн всегда бы совпадал, делая функцию всегда возвращающей
// `Ok(_)` независимо от ввода.
assert_eq!(check_value(42), Ok(42));
assert_eq!(check_value(43), Err(43));
}

Ошибкой является указание ref или ref mut, когда идентификатор затеняет константу.

Идентификаторные паттерны неопровержимы, если подпаттерн @ неопровержим или подпаттерн не указан.

Режимы привязки

Для лучшей эргономики паттерны работают в разных режимах привязки, чтобы упростить привязку ссылок к значениям. Когда ссылочное значение сопоставляется не ссылочным паттерном, оно будет автоматически обрабатываться как привязка ref или ref mut. Пример:

#![allow(unused)]
fn main() {
let x: &Option<i32> = &Some(3);
if let Some(y) = x {
    // y был преобразован в `ref y` и его тип &i32
}
}

Нессылочные паттерны включают все паттерны, кроме привязок, подстановочных паттернов (_), const паттернов ссылочных типов и ссылочных паттернов.

Если паттерн привязки явно не имеет ref, ref mut или mut, то он использует режим привязки по умолчанию для определения того, как переменная привязывается.

Режим привязки по умолчанию начинается в режиме “move”, который использует семантику перемещения.

При сопоставлении паттерна компилятор начинает снаружи паттерна и работает внутрь.

Каждый раз, когда ссылка сопоставляется с использованием нессылочного паттерна, она автоматически разыменовывает значение и обновляет режим привязки по умолчанию.

Ссылки устанавливают режим привязки по умолчанию в ref.

Изменяемые ссылки устанавливают режим в ref mut, если только режим уже не ref, в этом случае он остаётся ref.

Если автоматически разыменованное значение всё ещё является ссылкой, оно разыменовывается, и этот процесс повторяется.

Паттерн привязки может явно указывать режим привязки ref или ref mut, или указывать изменяемость с помощью mut, только когда режим привязки по умолчанию — “move”. Например, это не принимается:

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

2024 Edition differences

До редакции 2024 года привязки могли явно указывать режим привязки ref или ref mut, даже когда режим привязки по умолчанию не был “move”, и они могли указывать изменяемость на таких привязках с помощью mut. В этих редакциях указание mut на привязке устанавливало режим привязки в “move” независимо от текущего режима привязки по умолчанию.

Аналогично, ссылочный паттерн может появляться только когда режим привязки по умолчанию — “move”. Например, это не принимается:

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

2024 Edition differences

До редакции 2024 года ссылочные паттерны могли появляться даже когда режим привязки по умолчанию не был “move”, и имели как эффект сопоставления с образцом, так и сброса режима привязки по умолчанию в “move”.

Привязки перемещения и ссылочные привязки могут смешиваться в одном паттерне. Это приведёт к частичному перемещению объекта, к которому привязаны, и объект нельзя будет использовать afterwards. Это применяется только если тип не может быть скопирован.

В примере ниже name перемещается из person. Попытка использовать person как целое или person.name приведёт к ошибке из-за частичного перемещения.

Пример:

#![allow(unused)]
fn main() {
struct Person {
   name: String,
   age: u8,
}
let person = Person{ name: String::from("John"), age: 23 };
// `name` перемещается из person и `age` ссылается
let Person { name, ref age } = person;
}

Подстановочный паттерн

Syntax
WildcardPattern_

Подстановочный паттерн (символ подчёркивания) сопоставляется с любым значением. Он используется для игнорирования значений, когда они не важны.

Внутри других паттернов он сопоставляется с одним полем данных (в отличие от .., который сопоставляется с оставшимися полями).

В отличие от идентификаторных паттернов, он не копирует, не перемещает и не заимствует значение, с которым сопоставляется.

Примеры:

#![allow(unused)]
fn main() {
let x = 20;
let (a, _) = (10, x);   // x всегда сопоставляется с _
assert_eq!(a, 10);

// игнорировать параметр функции/замыкания
let real_part = |a: f64, _: f64| { a };

// игнорировать поле из структуры
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);

// принять любой Some, с любым значением
let x = Some(10);
if let Some(_) = x {}
}

Подстановочный паттерн всегда неопровержим.

Остаточный паттерн

Syntax
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 {
    // Игнорировать всё, кроме последнего элемента, который должен быть "!".
    [.., "!"] => println!("!!!"),

    // `start` - это срез всего, кроме последнего элемента, который должен быть "z".
    [start @ .., "z"] => println!("starts with: {:?}", start),

    // `end` - это срез всего, кроме первого элемента, который должен быть "a".
    ["a", end @ ..] => println!("ends with: {:?}", end),

    // 'whole' - это весь срез, а `last` - конечный элемент
    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);
// Остаточный паттерн также может использоваться в кортежных и кортежно-структурных паттернах.
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.

Граница записывается как один из:

  • Символьный, байтовый, целочисленный или плавающий литерал.
  • -, за которым следует целочисленный или плавающий литерал.
  • Путь.

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

// использование путей к константам:
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;
}
// использование квалифицированных путей:
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 Scalar Values: '\u{0000}'..='\u{D7FF}' и '\u{E000}'..='\u{10FFFF}'.

RangeFromPattern не может использоваться как паттерн верхнего уровня для подпаттернов в срезовых паттернах. Например, паттерн [1.., _] не является допустимым паттерном.

2021 Edition differences

До редакции 2021 года паттерны диапазонов с обеими границами также могли записываться с использованием ... вместо ..=, с тем же значением.

Ссылочные паттерны

Syntax
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 разыменовывает изменяемую ссылку. Изменяемость должна совпадать с изменяемостью ссылки.

Ссылочные паттерны всегда неопровержимы.

Структурные паттерны

Syntax
StructPattern
    PathInExpression {
        StructPatternElements?
    }

StructPatternElements
      StructPatternFields ( , | , StructPatternEtCetera )?
    | StructPatternEtCetera

StructPatternFields
    StructPatternField ( , StructPatternField )*

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

StructPatternEtCetera..

Структурные паттерны сопоставляются со значениями структур, перечислений и объединений, которые соответствуют всем критериям, определённым их подпаттернами. Они также используются для деструктуризации значения структуры, перечисления или объединения.

В структурном паттерне поля ссылаются по имени, индексу (в случае кортежных структур) или игнорируются с помощью ..:

#![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} => (),    // порядок не имеет значения
    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} => (),   // порядок не имеет значения
    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. Квалификаторы ref и mut могут быть включены с поведением, описанным в 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 для более подробной информации.

Кортежно-структурные паттерны

Кортежно-структурные паттерны сопоставляются со значениями кортежных структур и перечислений, которые соответствуют всем критериям, определённым их подпаттернами. Они также используются для деструктуризации значения кортежной структуры или перечисления.

Кортежно-структурный паттерн опровержим, если PathInExpression разрешается в конструктор перечисления с более чем одним вариантом, или один из его подпаттернов опровержим.

Кортежно-структурный паттерн сопоставляется с кортежной структурой или кортежным вариантом перечисления, конструктор которого разрешается из PathInExpression в пространстве имён значений.

Note

И наоборот, структурный паттерн для кортежной структуры или кортежного варианта перечисления, например S { 0: _ }, сопоставляется с кортежной структурой или вариантом, конструктор которого разрешается в пространстве имён типов.

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

// Импортировать `E1::V` только из пространства имён типов.
mod _0 {
    const V: () = (); // Для маскирования пространства имён.
    pub(super) use super::E1::*;
}
use _0::*;

// Импортировать `E2::V` только из пространства имён значений.
mod _1 {
    struct V {} // Для маскирования пространства имён.
    pub(super) use super::E2::*;
}
use _1::*;

fn f() {
    // Этот структурный паттерн сопоставляется с кортежным
    // вариантом перечисления, конструктор которого был найден в пространстве имён
    // типов.
    let V { 0: ..=u16::MAX } = (loop {}) else { loop {} };
    // Этот кортежно-структурный паттерн сопоставляется с кортежным
    // вариантом перечисления, конструктор которого был найден в пространстве имён
    // значений.
    let V(..=u32::MAX) = (loop {}) else { loop {} };
}
// Требуется из-за необычного поведения `super` внутри функций.
fn main() {}

Языковая команда приняла определённые решения, такие как в PR #138458, которые поднимают вопросы о желательности использования пространства имён значений таким образом для паттернов, как описано в PR #140593. Возможно, благоразумно не полагаться на этот нюанс в вашем коде.

Кортежные паттерны

Syntax
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");
}

Группированные паттерны

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() {
// Фиксированный размер
let arr = [1, 2, 3];
match arr {
    [1, _, _] => "starts with one",
    [a, b, c] => "starts with something else",
};
}
#![allow(unused)]
fn main() {
// Динамический размер
let v = vec![1, 2, 3];
match v[..] {
    [a, b] => { /* эта ветка не применится, потому что длина не совпадает */ }
    [a, b, c] => { /* эта ветка применится */ }
    _ => { /* этот подстановочный знак требуется, поскольку длина не известна статически */ }
};
}

Срезовые паттерны неопровержимы при сопоставлении с массивом, пока каждый элемент неопровержим.

При сопоставлении со срезом они неопровержимы только в форме с единственным .. остаточным паттерном или идентификаторным паттерном с .. остаточным паттерном в качестве подпаттерна.

Внутри среза паттерн диапазона без обеих границ должен быть заключён в круглые скобки, как в (a..), чтобы прояснить, что он предназначен для сопоставления с одним элементом среза. Паттерн диапазона с обеими границами, такой как a..=b, не требуется заключать в круглые скобки.

Путевые паттерны

Syntax
PathPatternPathExpression

Путевые паттерны — это паттерны, которые ссылаются либо на константные значения, либо на структуры или варианты перечислений, которые не имеют полей.

Неквалифицированные путевые паттерны могут ссылаться на:

  • варианты перечислений
  • структуры
  • константы
  • ассоциированные константы

Квалифицированные путевые паттерны могут ссылаться только на ассоциированные константы.

Путевые паттерны неопровержимы, когда они ссылаются на структуры или вариант перечисления, когда перечисление имеет только один вариант, или на константу, тип которой неопровержим. Они опровержимы, когда ссылаются на опровержимые константы или варианты перечислений для перечислений с несколькими вариантами.

Константные паттерны

Когда константа C типа T используется как паттерн, мы сначала проверяем, что T: PartialEq.

Кроме того, мы требуем, чтобы значение C имело (рекурсивное) структурное равенство, которое рекурсивно определяется следующим образом:

  • Целые числа, а также значения str, bool и char всегда имеют структурное равенство.
  • Кортежи, массивы и срезы имеют структурное равенство, если все их поля/элементы имеют структурное равенство. (В частности, () и [] всегда имеют структурное равенство.)
  • Ссылки имеют структурное равенство, если значение, на которое они указывают, имеет структурное равенство.
  • Значение типа struct или enum имеет структурное равенство, если его экземпляр PartialEq является производным через #[derive(PartialEq)], и все поля (для перечислений: активного варианта) имеют структурное равенство.
  • Необработанный указатель имеет структурное равенство, если он был определён как целочисленная константа (и затем приведён/трансмутирован).
  • Плавающее значение имеет структурное равенство, если оно не NaN.
  • Ничто другое не имеет структурного равенства.

В частности, значение C должно быть известно во время построения паттерна (что происходит до мономорфизации). Это означает, что ассоциированные константы, включающие обобщённые параметры, не могут использоваться как паттерны.

Значение C не должно содержать никаких ссылок на изменяемые статические переменные (static mut элементы или внутренне изменяемые static элементы) или extern статические переменные.

После обеспечения выполнения всех условий значение константы преобразуется в паттерн и теперь ведёт себя точно так, как если бы этот паттерн был написан напрямую. В частности, он полностью участвует в проверке исчерпываемости. (Для необработанных указателей константы — единственный способ записать такие паттерны. Только _ когда-либо считается исчерпывающим для этих типов.)

ИЛИ-паттерны

ИЛИ-паттерны — это паттерны, которые сопоставляются с одним из двух или более подпаттернов (например, A | B | C). Они могут вкладываться произвольно. Синтаксически ИЛИ-паттерны разрешены в любом из мест, где разрешены другие паттерны (представленные продукцией Pattern), за исключением let-привязок и аргументов функций и замыканий (представленных продукцией PatternNoTopAlt).

Статическая семантика

  1. Для паттерна p | q на некоторой глубине для произвольных паттернов p и q паттерн считается некорректным, если:

    • тип, выведенный для p, не унифицируется с типом, выведенным для q, или
    • один и тот же набор привязок не вводится в p и q, или
    • тип любых двух привязок с одним и тем же именем в p и q не унифицируется относительно типов или режимов привязки.

    Унификация типов во всех упомянутых случаях точна, и неявные приведения типов не применяются.

  1. При проверке типа выражения match e_s { a_1 => e_1, ... a_n => e_n }, для каждой ветки match a_i, которая содержит паттерн формы p_i | q_i, паттерн p_i | q_i считается некорректным, если на глубине d, где он существует фрагмент e_s на глубине d, тип фрагмента выражения не унифицируется с 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 с паттерном c(p | q, ..rest) на глубине d, где c — некоторый конструктор, p и q — произвольные паттерны, а rest — опционально любые оставшиеся потенциальные факторы в c, определяется как такая же, как у c(p, ..rest) | c(q, ..rest).

Приоритет с другими неразделёнными паттернами

Как показано в других местах этой главы, существует несколько типов паттернов, которые синтаксически неразделённы, включая идентификаторные паттерны, ссылочные паттерны и ИЛИ-паттерны. ИЛИ-паттерны всегда имеют самый низкий приоритет. Это позволяет нам зарезервировать синтаксическое пространство для возможной будущей функции указания типа и также уменьшить неоднозначность. Например, x @ A(..) | B(..) приведёт к ошибке, что x не привязан во всех паттернах. &A(x) | B(x) приведёт к несоответствию типа между x в разных подпаттернах.


  1. Синтаксис ObsoleteRangePattern был удалён в редакции 2021.