1. 1. Introduction
  2. 2. Введение
  3. 3. C чего начать
  4. 4. Изучение Rust
    1. 4.1. Угадайка
    2. 4.2. Обедающие философы
    3. 4.3. Вызов кода на Rust из других языков
  5. 5. Синтаксис и семантика
    1. 5.1. Связывание имён
    2. 5.2. Функции
    3. 5.3. Простые типы
    4. 5.4. Комментарии
    5. 5.5. Конструкция `if`
    6. 5.6. Циклы
    7. 5.7. Владение
    8. 5.8. Ссылки и заимствование
    9. 5.9. Время жизни
    10. 5.10. Изменяемость
    11. 5.11. Структуры
    12. 5.12. Перечисления
    13. 5.13. Конструкция `match`
    14. 5.14. Шаблоны сопоставления `match`
    15. 5.15. Синтаксис методов
    16. 5.16. Вектора
    17. 5.17. Строки
    18. 5.18. Обобщённое программирование
    19. 5.19. Типажи
    20. 5.20. Типаж `Drop`
    21. 5.21. Конструкция `if let`
    22. 5.22. Типажи-объекты
    23. 5.23. Замыкания
    24. 5.24. Универсальный синтаксис вызова функций
    25. 5.25. Контейнеры и модули
    26. 5.26. `const` и `static`
    27. 5.27. Атрибуты
    28. 5.28. Псевдонимы типов
    29. 5.29. Приведение типов
    30. 5.30. Ассоциированные типы
    31. 5.31. Безразмерные типы
    32. 5.32. Перегрузка операций
    33. 5.33. Преобразования при разыменовании
    34. 5.34. Макросы
    35. 5.35. Сырые указатели
    36. 5.36. Небезопасный код
  6. 6. Эффективное использование Rust
    1. 6.1. Стек и куча
    2. 6.2. Тестирование
    3. 6.3. Условная компиляция
    4. 6.4. Документация
    5. 6.5. Итераторы
    6. 6.6. Многозадачность
    7. 6.7. Обработка ошибок
    8. 6.8. Выбор гарантий
    9. 6.9. Интерфейс внешних функций
    10. 6.10. Типажи `Borrow` и `AsRef`
    11. 6.11. Каналы сборок
    12. 6.12. Using Rust without the standard library
  7. 7. Нестабильные возможности Rust
    1. 7.1. Плагины к компилятору
    2. 7.2. Встроенный ассемблерный код
    3. 7.3. Без stdlib
    4. 7.4. Внутренние средства
    5. 7.5. Элементы языка
    6. 7.6. Продвинутое руководство по компоновке
    7. 7.7. Тесты производительности
    8. 7.8. Синтаксис упаковки и шаблоны `match`
    9. 7.9. Шаблоны `match` для срезов
    10. 7.10. Ассоциированные константы
    11. 7.11. Пользовательские менеджеры памяти
  8. 8. Глоссарий
  9. 9. Syntax Index
  10. 10. Библиография

Шаблоны сопоставления `match`

Шаблоны достаточно часто используются в Rust. Мы уже использовали их в разделе Связывание переменных, в разделе Конструкция match, а также в некоторых других местах. Давайте коротко пробежимся по всем возможностям, которые можно реализовать с помощью шаблонов!

Быстро освежим в памяти: сопоставлять с шаблоном литералы можно либо напрямую, либо с использованием символа _, который означает любой случай:

let x = 1;

match x {
    1 => println!("один"),
    2 => println!("два"),
    3 => println!("три"),
    _ => println!("что угодно"),
}Run

Этот код напечатает один.

Сопоставление с несколькими шаблонами

Вы можете сопоставлять с несколькими шаблонами, используя |:

let x = 1;

match x {
    1 | 2 => println!("один или два"),
    3 => println!("три"),
    _ => println!("что угодно"),
}Run

Этот код напечатает один или два.

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

Если вы работаете с составным типом данных, вроде struct, вы можете разобрать его на части («деструктурировать») внутри шаблона:

struct Point {
    x: i32,
    y: i32,
}

let origin = Point { x: 0, y: 0 };

match origin {
    Point { x, y } => println!("({},{})", x, y),
}Run

Мы можем использовать :, чтобы привязать значение к новому имени.

struct Point {
    x: i32,
    y: i32,
}

let origin = Point { x: 0, y: 0 };

match origin {
    Point { x: x1, y: y1 } => println!("({},{})", x1, y1),
}Run

Если нас интересуют только некоторые значения, мы можем не давать имена всем составляющим:

struct Point {
    x: i32,
    y: i32,
}

let origin = Point { x: 0, y: 0 };

match origin {
    Point { x, .. } => println!("x равен {}", x),
}Run

Этот код напечатает x равен 0.

Вы можете использовать это в любом сопоставлении: не обязательно игнорировать именно первый элемент:

struct Point {
    x: i32,
    y: i32,
}

let origin = Point { x: 0, y: 0 };

match origin {
    Point { y, .. } => println!("y равен {}", y),
}Run

Этот код напечатает y равен 0.

Можно произвести деструктуризацию любого составного типа данных — например, кортежей и перечислений.

Игнорирование связывания

Вы можете использовать в шаблоне _, чтобы проигнорировать соответствующее значение. Например, вот сопоставление Result<T, E>:

match some_value {
    Ok(value) => println!("получили значение: {}", value),
    Err(_) => println!("произошла ошибка"),
}Run

В первой ветви мы привязываем значение варианта Ok к имени value. А в ветви обработки варианта Err мы используем _, чтобы проигнорировать конкретную ошибку, и просто печатаем общее сообщение.

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

fn coordinate() -> (i32, i32, i32) {
    // создаём и возвращаем какой-то кортеж из трёх элементов
}

let (x, _, z) = coordinate();Run

Здесь мы связываем первый и последний элемент кортежа с именами x и z соответственно, а второй элемент игнорируем.

Похожим образом, в шаблоне можно использовать .., чтобы проигнорировать несколько значений.

enum OptionalTuple {
    Value(i32, i32, i32),
    Missing,
}

let x = OptionalTuple::Value(5, -2, 3);

match x {
    OptionalTuple::Value(..) => println!("Получили кортеж!"),
    OptionalTuple::Missing => println!("Вот неудача."),
}Run

Этот код печатает Получили кортеж!.

ref и ref mut

Если вы хотите получить ссылку, то используйте ключевое слово ref:

let x = 5;

match x {
    ref r => println!("Получили ссылку на {}", r),
}Run

Этот код напечатает Получили ссылку на 5.

Здесь r внутри match имеет тип &i32. Другими словами, ключевое слово ref создает ссылку, для использования в шаблоне. Если вам нужна изменяемая ссылка, то ref mut будет работать аналогичным образом:

let mut x = 5;

match x {
    ref mut mr => println!("Получили изменяемую ссылку на {}", mr),
}Run

Сопоставление с диапазоном

Вы можете сопоставлять с диапазоном значений, используя ...:

let x = 1;

match x {
    1 ... 5 => println!("от одного до пяти"),
    _ => println!("что угодно"),
}Run

Этот код напечатает от одного до пяти.

Диапазоны в основном используются с числами или одиночными символами (char).

let x = '💅';

match x {
    'а' ... 'и' => println!("ранняя буква"),
    'к' ... 'я' => println!("поздняя буква"),
    _ => println!("что-то ещё"),
}Run

Этот код напечатает что-то ещё.

Связывание

Вы можете связать значение с именем с помощью символа @:

let x = 1;

match x {
    e @ 1 ... 5 => println!("получили элемент диапазона {}", e),
    _ => println!("что угодно"),
}Run

Этот код напечатает получили элемент диапазона 1. Это полезно, когда вы хотите сделать сложное сопоставление для части структуры данных:

#[derive(Debug)]
struct Person {
    name: Option<String>,
}

let name = "Steve".to_string();
let mut x: Option<Person> = Some(Person { name: Some(name) });
match x {
    Some(Person { name: ref a @ Some(_), .. }) => println!("{:?}", a),
    _ => {}
}Run

Этот код напечатает Some("Steve"): мы связали внутреннюю name с a.

Если вы используете @ совместно с |, то вы должны убедиться, что имя связывается в каждой из частей шаблона:

let x = 5;

match x {
    e @ 1 ... 5 | e @ 8 ... 10 => println!("получили элемент диапазона {}", e),
    _ => println!("что угодно"),
}Run

Ограничители шаблонов

Вы можете ввести ограничители шаблонов (match guards) с помощью if:

enum OptionalInt {
    Value(i32),
    Missing,
}

let x = OptionalInt::Value(5);

match x {
    OptionalInt::Value(i) if i > 5 => println!("Получили целое больше пяти!"),
    OptionalInt::Value(..) => println!("Получили целое!"),
    OptionalInt::Missing => println!("Неудача."),
}Run

Этот код напечатает Получили целое!.

Если вы используете if с несколькими шаблонами, он применяется к обеим частям:

let x = 4;
let y = false;

match x {
    4 | 5 if y => println!("да"),
    _ => println!("нет"),
}Run

Этот код печатает нет, потому что if применяется ко всему 4 | 5, а не только к 5. Другими словами, приоритет if выглядит так:

(4 | 5) if y => ...

а не так:

4 | (5 if y) => ...

Заключение

Вот так! Существует много разных способов использования конструкции сопоставления с шаблоном, и все они могут быть смешаны и состыкованы, в зависимости от того, что вы хотите сделать:

match x {
    Foo { x: Some(ref name), y: None } => ...
}Run

Шаблоны — это очень мощный инструмент. Используйте их.