# Сопоставление с образцом (Pattern Matching) в Rust

> Краткое и наглядное руководство по синтаксису match и pattern matching в языке Rust с примерами разбора значений, guard-условий и деструктуризации.

- **URL:** https://eagle2000.space/rust/match/
- **Date:** 2024-03-16

- **Tags:** rust, dev

---## Введение

В языке программирования Rust, `match` является мощной конструкцией, которая позволяет сравнивать значение с серией шаблонов и выполнять код в зависимости от совпадения. Рассмотрим различные виды и применения `match`.

## Базовый синтаксис, деструктуризация и условия

Конструкция `match` умеет значительно больше, чем простое сравнение числа с литералами. Она умеет деструктурировать сложные типы данных и уточнять шаблоны с помощью дополнительных условий — Match Guards.

Давайте разберём основные приёмы на интерактивном слайде:


### 💻 Основы match: синтаксис, деструктуризация, Match Guards


#### Шаг 1

```rust
fn main() {
    let season = "лето";

    match season {
        "весна"  => println!("Весна — всё цветёт"),
        "лето"   => println!("Лето — самое тёплое время"),
        "осень"  => println!("Осень — время листопада"),
        "зима"   => println!("Зима — время снега"),
        _        => println!("Неизвестное время года"),
    }
}
```

**Базовый синтаксис match**
- Конструкция `match` сравнивает значение с каждой «веткой» по порядку и выполняет код первого совпадения.
- Паттерн `_` — специальный «заглушитель», который срабатывает, если ни один другой шаблон не совпал. Rust требует, чтобы все возможные значения были обработаны.


#### Шаг 2

```rust
fn main() {
    let pair = (0, -2);

    match pair {
        (first, second) if first == 0 => println!("Первый — 0, второй: {}", second),
        (first, second) if second == 0 => println!("Первый: {}, второй — 0", first),
        _                              => println!("Другая комбинация"),
    }
}
```

**Match с деструктуризацией кортежей**
- `match` умеет разбирать (деструктурировать) сложные типы данных, например кортежи.
- Здесь `(first, second) if first == 0` совпадает с любым кортежем, где первый элемент равен `0`, и связывает оба элемента с переменными.


#### Шаг 3

```rust
fn main() {
    let pair = (4, 7);

    match pair {
        (x, y) if x == y     => println!("Числа одинаковые: {} и {}", x, y),
        (x, y) if x + y == 0 => println!("{} и {} — противоположны (сумма = 0)", x, y),
        (x, _) if x % 2 == 0 => println!("Первое число {} — чётное", x),
        _                    => println!("Нет особой связи"),
    }
}
```

**Match с условиями (Match Guards)**
- После шаблона можно дополнительно указать условие с помощью ключевого слова `if`.
- Ветка срабатывает только если шаблон совпал **и** условие выполняется.
- Это позволяет точно и лаконично фильтровать конкретные сценарии.


## Match с перечислениями (Enums)

`match` особенно мощен в работе с перечислениями. Варианты enum могут не только быть простыми маркерами, но и хранить данные — и `match` позволяет их безопасно извлечь прямо в ветке шаблона.


### 💻 Match с перечислениями: Color и Exercise


#### Шаг 1

```rust
#[allow(dead_code)]
enum Color {
    Red,
    Blue,
    Green,
    RGB(u32, u32, u32),
}

fn main() {
    let color = Color::RGB(122, 17, 40);

    match color {
        Color::Red   => println!("Красный"),
        Color::Blue  => println!("Синий"),
        Color::Green => println!("Зелёный"),
        Color::RGB(r, g, b) => println!("RGB: {}, {}, {}", r, g, b),
    }
}
```

**Match с перечислениями (Enums)**
- `match` — основной инструмент для работы с перечислениями (enum) в Rust.
- Каждая ветка соответствует одному из вариантов enum. Компилятор проверяет, что все варианты обработаны.
- Вариант `Color::RGB(r, g, b)` извлекает (деструктурирует) числа из данных, хранящихся внутри этого варианта enum.


#### Шаг 2

```rust
#[allow(dead_code)]
enum Exercise {
    Run { distance: f64 },
    Swim { duration: u32 },
    Yoga,
}

fn process_exercise(exercise: Exercise) {
    match exercise {
        Exercise::Run { distance } => {
            println!("Ты пробежал {} км. Отлично!", distance);
        }
        Exercise::Swim { duration } => {
            println!("Ты плавал {} минут. Продолжай в том же духе!", duration);
        }
        Exercise::Yoga => {
            println!("Отличная работа — норма йоги на сегодня выполнена!");
        }
    }
}

fn main() {
    process_exercise(Exercise::Run { distance: 10.5 });
    process_exercise(Exercise::Swim { duration: 45 });
    process_exercise(Exercise::Yoga);
}
```

**Match с богатыми данными внутри Enum**
- Варианты enum могут хранить именованные поля (как в структуре).
- В шаблоне `Exercise::Run { distance }` мы одновременно проверяем вариант и извлекаем поле `distance`.
- Это позволяет безопасно и элегантно обработать разные «формы» одного типа.


Шаблоны `match` позволяют обрабатывать различные варианты и извлекать связанные данные — безопасно и эффективно. Это даёт нам возможность легко обрабатывать различные случаи и извлекать данные из сложных типов, сохраняя при этом чистоту и ясность кода.

Когда мы используем `match`, мы не только упрощаем наш код, но и делаем его более надёжным. Компилятор гарантирует, что мы обработали все возможные варианты — иначе код просто не скомпилируется.

## Заключение

`match` в Rust — это мощный инструмент, который делает код более ясным и безопасным. Он позволяет обрабатывать различные случаи и извлекать данные из сложных типов в удобной и безопасной манере.

Также приятно отметить, что механизм паттерн-матчинга появился начиная с версии 3 в Dart (там используется ключевое слово `switch`) и активно применяется в языке Elixir.

---

## Проверь свои знания!

Пройдите короткий интерактивный тест, чтобы закрепить понимание Pattern Matching в Rust.


### 📝 Pattern Matching в Rust


**1. Что произойдёт, если не обработать все возможные варианты в match-выражении?**

- [ ] Программа скомпилируется, но упадёт с паникой во время выполнения
- [ ] Компилятор выдаст ошибку — match должен быть исчерпывающим
- [ ] Необработанные варианты будут просто проигнорированы
- [ ] Программа скомпилируется с предупреждением, но будет работать

**2. Что делает паттерн `_` в конструкции match?**

- [ ] Прерывает выполнение match и возвращает ошибку
- [ ] Совпадает с любым значением и «поглощает» все оставшиеся варианты
- [ ] Совпадает только с нулём или пустым значением
- [ ] Объявляет новую переменную с именем `_`

**3. Как называется дополнительное условие в ветке match, записанное после шаблона через ключевое слово `if`?**

- [ ] Match filter
- [ ] Arm predicate
- [ ] Match Guard (охранное условие)
- [ ] Pattern constraint

**4. Что происходит при деструктуризации в ветке match?**

- [ ] Ошибка компиляции, так как нельзя сравнивать кортеж с нулём
- [ ] Напечатает `other`, так как 5 не равно 0
- [ ] Напечатает `x = 5`, так как второй элемент равен 0
- [ ] Напечатает `x = 0`

**5. Какой механизм позволяет безопасно извлечь данные из конкретного варианта enum с помощью match?**

- [ ] Приведение типов через оператор `as`
- [ ] Деструктуризация в ветке match (например, `Variant { field }` или `Variant(val)`)
- [ ] Метод `.unwrap()`, вызванный на enum
- [ ] Цикл `for` по всем вариантам enum


