Реализации по умолчанию
Часть раздела Object Oriented Programming путешествия по Rust на Coddy — урок 31 из 61.
До сих пор каждый метод типажа, который мы определяли, был просто сигнатурой — требованием без тела. Но что, если большинство типов, реализующих типаж, будут использовать одну и ту же логику? Rust позволяет предоставить реализацию по умолчанию непосредственно в определении типажа.
Вместо того чтобы завершать сигнатуру метода точкой с запятой, вы добавляете тело:
trait Greet {
fn greet(&self) -> String {
String::from("Hello there!")
}
}
Теперь любой тип, реализующий Greet, автоматически получает этот метод — без написания какого-либо кода в блоке impl:
struct Guest;
impl Greet for Guest {}
let visitor = Guest;
println!("{}", visitor.greet()); // Привет!
Пустая реализация impl Greet for Guest {} допустима, так как типаж уже предоставляет поведение. Структура просто «подписывается» на типаж и получает метод по умолчанию бесплатно.
Этот паттерн полезен, когда вы хотите определить общее поведение, которое подходит для большинства случаев. Типы, которым требуется что-то другое, могут переопределить поведение по умолчанию (о чем вы узнаете далее), в то время как типам, которых устраивает стандартное поведение, не нужно повторяться.
Задание
ЛегкоДавайте изучим возможности реализаций по умолчанию, создав систему уведомлений! Вы создадите трейт, который предоставляет стандартное поведение «из коробки», чтобы типы могли использовать его, не создавая повторяющийся код.
Вы организуете свой код в трех файлах:
notifiable.rs: Определите публичный трейтNotifiableс методомnotify(&self) -> String, который имеет реализацию по умолчанию. По умолчанию он должен возвращать"You have a new notification!". Любой тип, реализующий этот трейт, автоматически получит это поведение без необходимости писать что-либо в своем блоке impl.alerts.rs: Создайте две простые публичные unit-структуры, которые реализуют ваш трейт:EmailAlert— реализуетNotifiableс пустым блоком impl (использует реализацию по умолчанию)SystemAlert— также реализуетNotifiableс пустым блоком impl (использует реализацию по умолчанию)
main.rs: Объедините ваши модули и продемонстрируйте, как оба типа алертов разделяют одно и то же поведение по умолчанию. Создайте экземпляры обеих структур и вызовите их методыnotify.
В вашем основном файле создайте EmailAlert и SystemAlert, затем выведите их уведомления на отдельных строках.
Ваш вывод должен быть таким:
You have a new notification!
You have a new notification!Обратите внимание, что оба типа выдают одинаковый результат — в этом и заключается прелесть реализаций по умолчанию! Ни одной из структур не пришлось писать код для метода notify, но обе имеют полностью функциональное поведение уведомлений. В следующем уроке вы узнаете, как переопределять эти значения по умолчанию, когда типу требуется кастомное поведение.
Шпаргалка
Методы типажа могут иметь реализации по умолчанию, если предоставить тело метода вместо одной лишь сигнатуры:
trait Greet {
fn greet(&self) -> String {
String::from("Hello there!")
}
}
Типы, реализующие этот типаж, автоматически получают поведение по умолчанию без написания какого-либо кода:
struct Guest;
impl Greet for Guest {}
let visitor = Guest;
println!("{}", visitor.greet()); // Hello there!
Пустой блок impl допустим, когда типаж предоставляет реализации по умолчанию. Это позволяет типам выбирать общее поведение без повторений.
Попробуйте сами
mod notifiable;
mod alerts;
use notifiable::Notifiable;
use alerts::{EmailAlert, SystemAlert};
fn main() {
// TODO: Создать экземпляр EmailAlert
// TODO: Создать экземпляр SystemAlert
// TODO: Вывести уведомление от EmailAlert
// TODO: Вывести уведомление от SystemAlert
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Object Oriented Programming
1Методы и поведение
Введение в блоки реализацииПараметр SelfИзменяемые методыАссоциированные функцииНесколько блоков реализацииЦепочки вызовов методовИтоги: действия с Rectangle4Проект: Виртуальный питомец
Определение питомцаКормление питомца2Инкапсуляция и модули
Основы модулейКлючевое слово PublicПриватные поляГеттерыСеттерыИтоги — Безопасный сейф5Обобщения
Обобщённые структурыОбобщённые методыНесколько обобщённых типовОбобщённые функцииПовторение — Координатная точка8Трейты как ограничения
Синтаксис ограничений трейтовМножественные ограниченияКонструкция whereВозврат типов с трейтамиИтоги — Обобщённый принтер11Паттерны проектирования в Rust
Паттерн NewtypeКомпозицияТрейт DropFrom и IntoИтоги — Мок-объект Smart Pointer3Продвинутые перечисления
Перечисления с даннымиМетоды в перечисленияхСопоставление вариантов с даннымиСнова об Option EnumИтоги — перечисление Shape6Определение трейтов
Что такое трейт?Реализация трейтовРеализации по умолчаниюПереопределение реализаций по умолчаниюТрейты с параметрамиИтоги — Media Player