Методы в перечислениях
Часть раздела Object Oriented Programming путешествия по Rust на Coddy — урок 15 из 61.
Так же, как и структуры, перечисления могут иметь методы, определенные в блоках impl. Это позволяет привязывать поведение непосредственно к вашему типу перечисления, упрощая работу с различными вариантами через единый интерфейс.
Вот как добавить метод к перечислению Message из предыдущего урока:
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
}
impl Message {
fn describe(&self) -> String {
match self {
Message::Quit => String::from("Quit signal"),
Message::Move { x, y } => format!("Move to ({}, {})", x, y),
Message::Write(text) => format!("Message: {}", text),
}
}
}
Блок impl Message работает точно так же, как и для структур. Метод принимает &self для доступа к экземпляру перечисления. Внутри метода вы используете match, чтобы определить, с каким вариантом вы имеете дело, и вернуть соответствующий результат.
Вызов метода выглядит одинаково независимо от того, какой вариант у вас есть:
fn main() {
let m1 = Message::Quit;
let m2 = Message::Move { x: 10, y: 20 };
println!("{}", m1.describe()); // Сигнал выхода
println!("{}", m2.describe()); // Перемещение в (10, 20)
}
Этот паттерн очень эффективен, потому что вызывающему коду не нужно знать, какой именно вариант он использует — он просто вызывает метод, а перечисление (enum) берет на себя остальное. Поведение меняется в зависимости от варианта, но интерфейс остается неизменным.
Задание
ЛегкоДавайте создадим систему светофора, которая использует перечисление (enum) с методами для описания текущего состояния сигнала. Каждый цвет светофора будет содержать информацию о том, как долго он длится, а метод будет предоставлять понятное человеку описание.
Вы создадите два файла для организации вашего кода:
traffic.rs: Определите публичное перечислениеTrafficLightс тремя вариантами:Red— содержитu32, представляющее секунды ожиданияYellow— содержитu32, представляющее секунды ожиданияGreen— содержитu32, представляющее секунды движения
status, который возвращаетStringс описанием сигнала. Используйтеmatchвнутри метода для обработки каждого варианта и включите длительность в сообщение.main.rs: Подключите ваш модуль traffic, создайте экземпляры каждого варианта светофора и вызовите методstatusдля каждого из них, чтобы вывести описания.
Метод status должен возвращать сообщения строго в следующих форматах:
- Для
Red:Stop for {seconds} seconds - Для
Yellow:Caution for {seconds} seconds - Для
Green:Go for {seconds} seconds
Ваш вывод должен отображать статус каждого сигнала на отдельной строке:
Stop for {seconds} seconds
Caution for {seconds} seconds
Go for {seconds} secondsНапример, при красном сигнале в 30 секунд, желтом в 5 секунд и зеленом в 25 секунд, вывод будет следующим:
Stop for 30 seconds
Caution for 5 seconds
Go for 25 secondsВы получите три входных значения: длительность красного сигнала, длительность желтого сигнала и длительность зеленого сигнала.
Шпаргалка
Перечисления могут иметь методы, определенные в impl блоках, точно так же, как и структуры:
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
}
impl Message {
fn describe(&self) -> String {
match self {
Message::Quit => String::from("Quit signal"),
Message::Move { x, y } => format!("Move to ({}, {})", x, y),
Message::Write(text) => format!("Message: {}", text),
}
}
}
Методы принимают &self для доступа к экземпляру перечисления и используют match для обработки различных вариантов:
let m1 = Message::Quit;
let m2 = Message::Move { x: 10, y: 20 };
println!("{}", m1.describe()); // Сигнал выхода
println!("{}", m2.describe()); // Перемещение в (10, 20)
Вызывающая сторона использует один и тот же интерфейс независимо от варианта — перечисление обрабатывает поведение внутри себя в зависимости от того, какой это вариант.
Попробуйте сами
mod traffic;
use traffic::TrafficLight;
fn main() {
// Чтение входных данных для длительности
let mut red_input = String::new();
std::io::stdin().read_line(&mut red_input).expect("Failed to read line");
let red_duration: u32 = red_input.trim().parse().expect("Invalid number");
let mut yellow_input = String::new();
std::io::stdin().read_line(&mut yellow_input).expect("Failed to read line");
let yellow_duration: u32 = yellow_input.trim().parse().expect("Invalid number");
let mut green_input = String::new();
std::io::stdin().read_line(&mut green_input).expect("Failed to read line");
let green_duration: u32 = green_input.trim().parse().expect("Invalid number");
// TODO: Создать экземпляры каждого варианта TrafficLight с введенными длительностями
// TODO: Вызвать метод status для каждого светофора и вывести результат
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Object Oriented Programming
1Методы и поведение
Введение в блоки реализацииПараметр SelfИзменяемые методыАссоциированные функцииНесколько блоков реализацииЦепочки вызовов методовИтоги: действия с Rectangle4Проект: Виртуальный питомец
Определение питомцаКормление питомца2Инкапсуляция и модули
Основы модулейКлючевое слово PublicПриватные поляГеттерыСеттерыИтоги — Безопасный сейф5Обобщения
Обобщённые структурыОбобщённые методыНесколько обобщённых типовОбобщённые функцииПовторение — Координатная точка8Трейты как ограничения
Синтаксис ограничений трейтовМножественные ограниченияКонструкция whereВозврат типов с трейтамиИтоги — Обобщённый принтер11Паттерны проектирования в Rust
Паттерн NewtypeКомпозицияТрейт DropFrom и IntoИтоги — Мок-объект Smart Pointer3Продвинутые перечисления
Перечисления с даннымиМетоды в перечисленияхСопоставление вариантов с даннымиСнова об Option EnumИтоги — перечисление Shape6Определение трейтов
Что такое трейт?Реализация трейтовРеализации по умолчаниюПереопределение реализаций по умолчаниюТрейты с параметрамиИтоги — Media Player