Переопределение реализаций по умолчанию
Часть раздела Object Oriented Programming путешествия по Rust на Coddy — урок 32 из 61.
Реализации по умолчанию удобны, но иногда типу требуется поведение, отличное от стандартного. В таких случаях вы можете переопределить поведение по умолчанию, предоставив собственную реализацию в блоке impl.
Просто определите метод с той же сигнатурой, но добавьте свою собственную логику:
trait Greet {
fn greet(&self) -> String {
String::from("Hello there!")
}
}
struct Robot {
id: u32,
}
impl Greet for Robot {
fn greet(&self) -> String {
format!("BEEP BOOP. Unit {} online.", self.id)
}
}
Хотя Greet предоставляет метод greet по умолчанию, Robot полностью заменяет его собственной версией. Когда вы вызываете greet() у Robot, Rust использует пользовательскую реализацию:
let bot = Robot { id: 42 };
println!("{}", bot.greet()); // БИП-БУП. Юнит 42 в сети.
Это дает вам гибкость — типам, которых устраивает поведение по умолчанию, не нужно ничего писать, в то время как типы, которым требуется специализированное поведение, могут переопределить только те методы, которые им важны. Трейт по-прежнему гарантирует, что у каждого реализующего типа этот метод будет доступен.
Задание
ЛегкоДавайте создадим систему уведомлений, в которой различные типы оповещений могут настраивать свои сообщения! Вы создадите трейт с реализацией по умолчанию, а затем сделаете так, чтобы один тип использовал поведение по умолчанию, в то время как другой переопределял его своим собственным поведением.
Вы организуете свой код в трех файлах:
notifiable.rs: Определите публичный трейтNotifiableс методомnotify(&self) -> String, который имеет реализацию по умолчанию, возвращающую"Alert: Something happened!".alerts.rs: Создайте две публичные структуры, которые по-разному реализуют ваш трейт:GenericAlert— юнит-структура, использующая уведомление по умолчанию (пустой блок impl)UrgentAlert— структура с публичным полемmessage(String), которая переопределяет поведение по умолчанию, возвращая"URGENT: {message}", где{message}— это сохраненное в ней сообщение
main.rs: Объедините ваши модули и продемонстрируйте оба варианта поведения. СоздайтеGenericAlertиUrgentAlert, используя предоставленные входные данные, а затем выведите их уведомления.
Ключевая идея здесь заключается в том, что GenericAlert получает готовое поведение по умолчанию, в то время как UrgentAlert предоставляет свою специализированную версию, определяя метод в своем блоке impl.
Ваш вывод должен соответствовать следующему формату:
Alert: Something happened!
URGENT: {message}Например, при входных данных Server is down!:
Alert: Something happened!
URGENT: Server is down!Вы получите одни входные данные: сообщение для срочного оповещения (urgent alert).
Шпаргалка
Вы можете переопределить реализации типажа по умолчанию, предоставив собственную реализацию в блоке impl:
trait Greet {
fn greet(&self) -> String {
String::from("Hello there!")
}
}
struct Robot {
id: u32,
}
impl Greet for Robot {
fn greet(&self) -> String {
format!("BEEP BOOP. Unit {} online.", self.id)
}
}
let bot = Robot { id: 42 };
println!("{}", bot.greet()); // BEEP BOOP. Unit 42 online.
Типы, которым не требуется пользовательское поведение, могут использовать пустой блок impl, чтобы принять реализацию по умолчанию, в то время как типы, требующие специализированного поведения, могут переопределять конкретные методы, определяя их с той же сигнатурой.
Попробуйте сами
mod notifiable;
mod alerts;
use alerts::{GenericAlert, UrgentAlert};
use notifiable::Notifiable;
fn main() {
// Считать ввод для сообщения срочного оповещения
let mut input = String::new();
std::io::stdin().read_line(&mut input).expect("Failed to read line");
let message = input.trim().to_string();
// TODO: Создать экземпляр GenericAlert
// TODO: Создать экземпляр UrgentAlert с введенным сообщением
// TODO: Вывести уведомление из GenericAlert
// TODO: Вывести уведомление из UrgentAlert
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Object Oriented Programming
1Методы и поведение
Введение в блоки реализацииПараметр SelfИзменяемые методыАссоциированные функцииНесколько блоков реализацииЦепочки вызовов методовИтоги: действия с Rectangle4Проект: Виртуальный питомец
Определение питомцаКормление питомца2Инкапсуляция и модули
Основы модулейКлючевое слово PublicПриватные поляГеттерыСеттерыИтоги — Безопасный сейф5Обобщения
Обобщённые структурыОбобщённые методыНесколько обобщённых типовОбобщённые функцииПовторение — Координатная точка8Трейты как ограничения
Синтаксис ограничений трейтовМножественные ограниченияКонструкция whereВозврат типов с трейтамиИтоги — Обобщённый принтер11Паттерны проектирования в Rust
Паттерн NewtypeКомпозицияТрейт DropFrom и IntoИтоги — Мок-объект Smart Pointer3Продвинутые перечисления
Перечисления с даннымиМетоды в перечисленияхСопоставление вариантов с даннымиСнова об Option EnumИтоги — перечисление Shape6Определение трейтов
Что такое трейт?Реализация трейтовРеализации по умолчаниюПереопределение реализаций по умолчаниюТрейты с параметрамиИтоги — Media Player