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`

Простого if/else часто недостаточно, потому что нужно проверить больше, чем два возможных варианта. Да и к тому же условия в else часто становятся очень сложными. Как же решить эту проблему?

В Rust есть ключевое слово match, позволяющее заменить группы операторов if/else чем-то более удобным. Смотрите:

let x = 5;

match x {
    1 => println!("один"),
    2 => println!("два"),
    3 => println!("три"),
    4 => println!("четыре"),
    5 => println!("пять"),
    _ => println!("что-то ещё"),
}Run

match принимает выражение и выбирает одну из ветвей исполнения согласно его значению. Каждая ветвь имеет форму значение => выражение. Выражение ветви вычисляется, когда значение данной ветви совпадает со значением, принятым оператором match (в данном случае, x). Эта конструкция называется match (сопоставление), потому что она выполняет сопоставление значения неким «шаблонам». Глава «Шаблоны» описывает все шаблоны, которые можно использовать в match.

Так в чём же преимущества данной конструкции? Их несколько. Во-первых, ветви match проверяются на полноту. Видите последнюю ветвь, со знаком подчёркивания (_)? Если мы удалим её, Rust выдаст ошибку:

error: non-exhaustive patterns: `_` not covered

Другими словами, компилятор сообщает нам, что мы забыли сопоставить какие-то значения. Поскольку x — это целое число, оно может принимать разные значения — например, 6. Однако, если мы убираем ветвь _, ни одна ветвь не совпадёт, поэтому такой код не скомпилируется. _ — это «совпадение с любым значением». Если ни одна другая ветвь не совпала, совпадёт ветвь с _. Поскольку в примере выше есть ветвь с _, мы покрываем всё множество значений x, и наша программа скомпилируется.

match также является выражением. Это значит, что мы можем использовать его в правой части оператора let или непосредственно как выражение:

let x = 5;

let numer = match x {
    1 => "one",
    2 => "two",
    3 => "three",
    4 => "four",
    5 => "five",
    _ => "something else",
};Run

Иногда с помощью match можно удобно преобразовать значения одного типа в другой.

Сопоставление с образцом для перечислений

Другой полезный способ использования match — обработка возможных вариантов перечисления:

enum Message {
    Quit,
    ChangeColor(i32, i32, i32),
    Move { x: i32, y: i32 },
    Write(String),
}

fn quit() { /* ... */ }
fn change_color(r: i32, g: i32, b: i32) { /* ... */ }
fn move_cursor(x: i32, y: i32) { /* ... */ }

fn process_message(msg: Message) {
    match msg {
        Message::Quit => quit(),
        Message::ChangeColor(r, g, b) => change_color(r, g, b),
        Message::Move { x: x, y: y } => move_cursor(x, y),
        Message::Write(s) => println!("{}", s),
    };
}Run

Как обычно, компилятор Rust проверяет полноту, поэтому в match должна быть ветвь для каждого варианта перечисления. Если какой-то вариант отсутствует, программа не скомпилируется и вам придётся использовать _.

Здесь мы не можем использовать обычный if вместо match, в отличие от кода, который мы видели раньше. Но мы могли бы использовать if let — его можно воспринимать как сокращённую форму записи match.