# Перечисления (Enums) в Rust: от синтаксиса до сопоставления

> Разбор перечислений (enums) в языке Rust: варианты с данными, незаселенные типы, паттерн Option и сопоставление с образцом match на реальных примерах.

- **URL:** https://eagle2000.space/rust/rust-enums/
- **Date:** 2026-08-01

- **Tags:** rust, dev

---## Введение. Что такое перечисление

**Перечисления (Enums)** в Rust — это тип данных, который позволяет описать сущность, принимающую одно из нескольких возможных значений (вариантов). 

Если вы раньше программировали, например, на Java или C++, то наверняка пользовались enums для создания списка констант (например, дней недели). Но в Rust перечисления намного мощнее! Они позволяют хранить разнородные данные внутри каждого варианта, благодаря чему код становится чистым и безопасным.

## Варианты конструирования (внутренний синтаксис)

В отличие от многих других языков, где перечисления — это просто список именованных целых чисел, в Rust варианты enums могут содержать в себе данные различного типа и структуры. Один `enum` может содержать три вида вариантов:

1. **Юнит-варианты:** без связанных данных (аналогично юнит-структурам).
2. **Кортежные варианты:** со связанными безымянными данными в круглых скобках (аналогично структурам-кортежам).
3. **Структурные варианты:** с именованными полями в фигурных скобках `{}` (аналогично обычным структурам).

Давайте посмотрим, как объявляются варианты, что такое незаселенный тип и как извлечь данные с помощью `match` на интерактивных слайдах ниже:


### 💻 Работа с перечислениями в Rust


#### Шаг 1

```rust
#[allow(dead_code)]
#[derive(Debug)]
enum WebEvent {
    // Юнит-вариант (без связанных данных)
    PageLoad,
    // Кортежный вариант (со связанными безымянными данными)
    KeyPress(char),
    // Структурный вариант (с именованными полями)
    Click { x: i64, y: i64 },
}

fn main() {
    let load = WebEvent::PageLoad;
    let press = WebEvent::KeyPress('q');
    let click = WebEvent::Click { x: 320, y: 240 };

    println!("load: {:?}", load);
    println!("press: {:?}", press);
    println!("click: {:?}", click);
}
```

**Варианты конструирования перечислений**
- Перечисления (enums) могут объединять данные разных форматов под одним типом.
- Юнит-варианты не несут связанных данных (`PageLoad`).
- Кортежные варианты хранят неименованные данные в круглых скобках (`KeyPress`).
- Структурные варианты хранят именованные поля в фигурных скобках (`Click`).


#### Шаг 2

```rust
// Определение незаселенного типа (uninhabited type)
// Перечисление без вариантов
#[allow(dead_code)]
enum EmptyEnum {}

fn main() {
    // Экземпляр EmptyEnum создать невозможно, так как нет вариантов!
    println!("EmptyEnum определен, но не может быть инициализирован.");
}
```

**Незаселенные типы (Uninhabited Types)**
- Перечисление без единого варианта называется незаселенным типом.
- Создать экземпляр такого типа в безопасном коде невозможно.
- Используется в системе типов для обозначения невозможных ситуаций (например, ошибки, которая никогда не произойдет).


#### Шаг 3

```rust
#[allow(dead_code)]
enum Message {
    Quit,
    Move { x: i32, y: i32 },
    Write(String),
}

fn main() {
    let msg = Message::Move { x: 10, y: 20 };

    // Извлечение данных через сопоставление паттернов (pattern matching)
    match msg {
        Message::Quit => {
            println!("Событие выхода");
        }
        Message::Move { x, y } => {
            println!("Перемещение в координаты x = {}, y = {}", x, y);
        }
        Message::Write(text) => {
            println!("Запись сообщения: {}", text);
        }
    }
}
```

**Извлечение данных с помощью match**
- Механизм `match` гарантирует безопасный доступ к данным внутри вариантов enums.
- Компилятор Rust требует обязательной обработки всех вариантов перечисления (исчерпывающее сопоставление).
- Данные деструктурируются прямо в шаблоне ветки `match`.


### Детали реализации:

* **Шаг 1. Варианты конструирования:** Мы создали перечисление `WebEvent`, демонстрирующее все три типа вариантов. Важная деталь: все поля и данные внутри вариантов перечисления **автоматически являются публичными**, писать `pub` перед ними не нужно.
* **Шаг 2. Пустые перечисления:** В Rust можно объявить `enum` вообще без вариантов (например, `enum EmptyEnum {}`). Создать объект такого типа в обычном коде невозможно. Зачем это нужно? Обычно так помечают состояния, которые никогда не должны произойти физически, помогая компилятору оптимизировать проверки.
* **Шаг 3. Достаем данные (Pattern Matching):** Так как разные варианты перечисления могут содержать абсолютно разные типы данных, компилятор не разрешит обратиться к ним напрямую. Нам нужно проверить, какой именно вариант сейчас активен. Самый удобный способ сделать это — конструкция `match`.

> **Важная деталь:** Конструкция `match` в Rust является исчерпывающей (exhaustive). Это означает, что компилятор выдаст ошибку, если хотя бы один из вариантов enums не будет обработан в ветках `match`.

---

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

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


### 📝 Перечисления в Rust


**1. Как ещё называют перечисления (Enums) в теории типов?**

- [ ] Тип-произведение (product type)
- [ ] Тип-сумма (sum type) или тегированное объединение (tagged union)
- [ ] Динамический интерфейс (dynamic interface)
- [ ] Абстрактный класс (abstract class)

**2. Какая видимость у полей (связанных данных) внутри вариантов перечисления по умолчанию?**

- [ ] Они автоматически являются приватными (private)
- [ ] Они автоматически являются публичными (public)
- [ ] Их видимость зависит от видимости самого перечисления
- [ ] Требуется явно указывать ключевое слово `pub` перед каждым полем варианта

**3. Какой из вариантов перечисления `enum Foo { A, B(u32), C { val: bool } }` является структурным вариантом?**

- [ ] A
- [ ] B(u32)
- [ ] C { val: bool }
- [ ] Ни один из перечисленных

**4. Что представляет собой незаселенный тип (uninhabited type) в виде enum?**

- [ ] Перечисление, у которого все варианты помечены как устаревшие (deprecated)
- [ ] Перечисление, которое вообще не содержит ни одного варианта
- [ ] Перечисление, содержащее только пустые юнит-варианты
- [ ] Специальное перечисление `Option::None`

**5. Как безопасно извлечь связанные данные из варианта перечисления?**

- [ ] С помощью приведения типов `as`
- [ ] С помощью механизма сопоставления паттернов (pattern matching), например, через `match`
- [ ] С помощью прямого обращения через точку к имени варианта (например, `event.KeyPress.0`)
- [ ] Связав данные с глобальной переменной

