Перечисления с данными
Часть раздела 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Вы получите три входных значения: сообщение для оповещения, заголовок напоминания и количество минут для напоминания.
Попробуйте сами
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