Menu
Coddy logo textTech

Перечисления с данными

Часть раздела Object Oriented Programming путешествия по Rust на Coddy — урок 14 из 61.

Базовые перечисления (enums) в Rust позволяют определять тип с фиксированным набором вариантов — таких как Direction::North или Status::Active. Но перечисления в Rust гораздо мощнее. Каждый вариант может содержать собственные данные, что делает перечисления невероятно гибкими для моделирования сценариев реального мира.

Рассмотрим систему обмена сообщениями, в которой разные типы сообщений несут разную информацию. Текстовое сообщение содержит контент, команда перемещения — координаты, а сигнал выхода не содержит ничего. В Rust вы можете представить всё это с помощью одного перечисления (enum):

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

У каждого варианта своя форма. Quit не содержит данных — это просто сигнал.

Move использует синтаксис, подобный структурам, с именованными полями. Write содержит одну String, используя синтаксис кортежей. ChangeColor содержит три значения, представляющие компоненты RGB.

Создание экземпляров этих вариантов выглядит так:

let m1 = Message::Quit;
let m2 = Message::Move { x: 10, y: 20 };
let m3 = Message::Write(String::from("Hello"));
let m4 = Message::ChangeColor(255, 128, 0);

Прелесть этого подхода заключается в том, что m1, m2, m3 и m4 имеют один и тот же тип: Message. Вы можете хранить их в одной коллекции, передавать их в одну и ту же функцию или возвращать любой вариант из одной функции. Перечисление (enum) объединяет различные формы данных под одним типом, сохраняя при этом специфическую информацию, необходимую каждому варианту.

challenge icon

Задание

Легко

Давайте создадим систему уведомлений, которая использует перечисления (enums) с данными для представления различных типов оповещений. Каждый тип уведомления несет разную информацию: некоторые содержат сообщения, некоторые — числовые значения, а некоторые являются простыми сигналами.

Вы создадите два файла для организации вашего кода:

  • notification.rs: Определите публичное перечисление Notification с тремя вариантами:
    • Alert — содержит одно сообщение типа String (синтаксис кортежа)
    • Reminder — содержит именованные поля: title (String) и minutes (u32)
    • Dismiss — не содержит данных, просто сигнал
  • main.rs: Подключите модуль уведомлений и создайте по одному экземпляру каждого варианта. Затем выведите информацию о каждом уведомлении, чтобы продемонстрировать, что все три варианта относятся к одному типу, но несут разные данные.

При выводе используйте форматирование для отладки ({:?}), чтобы отобразить каждое уведомление. Вам нужно будет реализовать (derive) типаж Debug для вашего перечисления, чтобы его можно было напечатать таким образом.

Ваш вывод должен содержать все три уведомления, каждое на новой строке:

Alert("{message}")
Reminder { title: "{title}", minutes: {minutes} }
Dismiss

Например, с сообщением об оповещении "Server down", напоминанием с заголовком "Meeting" и 30 минутами, вывод будет следующим:

Alert("Server down")
Reminder { title: "Meeting", minutes: 30 }
Dismiss

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

Шпаргалка

Варианты перечислений (Enum) в Rust могут содержать данные различных типов, что делает их мощным инструментом для моделирования разнообразных сценариев в рамках одного типа.

Определение перечислений с данными

Каждый вариант может иметь свою собственную структуру данных:

enum Message {
    Quit,                          // Без данных
    Move { x: i32, y: i32 },      // Именованные поля (подобно структуре)
    Write(String),                 // Одиночное значение (синтаксис кортежа)
    ChangeColor(u8, u8, u8),      // Несколько значений (синтаксис кортежа)
}

Создание экземпляров перечисления

let m1 = Message::Quit;
let m2 = Message::Move { x: 10, y: 20 };
let m3 = Message::Write(String::from("Hello"));
let m4 = Message::ChangeColor(255, 128, 0);

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

Трейт Debug

Чтобы выводить перечисления с использованием форматирования {:?}, реализуйте трейт Debug с помощью макроса derive:

#[derive(Debug)]
enum Message {
    // варианты...
}

Попробуйте сами

mod notification;

use notification::Notification;

fn main() {
    // Чтение входных данных
    let mut alert_message = String::new();
    std::io::stdin().read_line(&mut alert_message).expect("Failed to read line");
    let alert_message = alert_message.trim().to_string();

    let mut reminder_title = String::new();
    std::io::stdin().read_line(&mut reminder_title).expect("Failed to read line");
    let reminder_title = reminder_title.trim().to_string();

    let mut minutes_input = String::new();
    std::io::stdin().read_line(&mut minutes_input).expect("Failed to read line");
    let reminder_minutes: u32 = minutes_input.trim().parse().expect("Failed to parse minutes");

    // TODO: Создайте уведомление Alert, используя alert_message

    // TODO: Создайте уведомление Reminder, используя reminder_title и reminder_minutes

    // TODO: Создайте уведомление Dismiss

    // TODO: Выведите каждое уведомление, используя форматирование отладки {:?}
    // Каждое уведомление должно быть выведено на отдельной строке
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Object Oriented Programming