Перечисления с данными
Часть раздела 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) объединяет различные формы данных под одним типом, сохраняя при этом специфическую информацию, необходимую каждому варианту.
Задание
ЛегкоДавайте создадим систему уведомлений, которая использует перечисления (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: Выведите каждое уведомление, используя форматирование отладки {:?}
// Каждое уведомление должно быть выведено на отдельной строке
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Object Oriented Programming
1Методы и поведение
Введение в блоки реализацииПараметр SelfИзменяемые методыАссоциированные функцииНесколько блоков реализацииЦепочки вызовов методовИтоги: действия с Rectangle4Проект: Виртуальный питомец
Определение питомцаКормление питомца2Инкапсуляция и модули
Основы модулейКлючевое слово PublicПриватные поляГеттерыСеттерыИтоги — Безопасный сейф5Обобщения
Обобщённые структурыОбобщённые методыНесколько обобщённых типовОбобщённые функцииПовторение — Координатная точка8Трейты как ограничения
Синтаксис ограничений трейтовМножественные ограниченияКонструкция whereВозврат типов с трейтамиИтоги — Обобщённый принтер11Паттерны проектирования в Rust
Паттерн NewtypeКомпозицияТрейт DropFrom и IntoИтоги — Мок-объект Smart Pointer3Продвинутые перечисления
Перечисления с даннымиМетоды в перечисленияхСопоставление вариантов с даннымиСнова об Option EnumИтоги — перечисление Shape6Определение трейтов
Что такое трейт?Реализация трейтовРеализации по умолчаниюПереопределение реализаций по умолчаниюТрейты с параметрамиИтоги — Media Player