Реализация трейтов
Часть раздела Object Oriented Programming путешествия по Rust на Coddy — урок 30 из 61.
Теперь, когда вы знаете, как определять типаж, давайте посмотрим, как заставить структуру соответствовать этому контракту. Реализация типажа связывает абстрактное требование с конкретным поведением.
Синтаксис использует impl TraitName for StructName:
trait Speak {
fn speak(&self) -> String;
}
struct Dog {
name: String,
}
impl Speak for Dog {
fn speak(&self) -> String {
format!("{} says: Woof!", self.name)
}
}
Блок impl Speak for Dog сообщает Rust, что Dog реализует типаж Speak. Внутри вы предоставляете фактическое тело метода — именно здесь вы определяете, как говорит собака.
Сила типажей становится очевидной, когда несколько типов реализуют один и тот же типаж:
struct Cat {
name: String,
}
impl Speak for Cat {
fn speak(&self) -> String {
format!("{} says: Meow!", self.name)
}
}
Теперь и Dog, и Cat обладают способностью Speak, но каждый предоставляет свою собственную уникальную реализацию. Вы можете вызвать .speak() для любого из этих типов:
let dog = Dog { name: String::from("Rex") };
let cat = Cat { name: String::from("Whiskers") };
println!("{}", dog.speak()); // Rex говорит: Woof!
println!("{}", cat.speak()); // Whiskers говорит: Meow!
Это основа полиморфизма в Rust — разные типы, один и тот же интерфейс, уникальное поведение.
Задание
ЛегкоДавайте воплотим типажи (traits) в жизнь, реализовав их для различных типов! Вы создадите типаж Describable и реализуете его для двух разных структур, каждая из которых будет предоставлять свое уникальное описание.
Вы организуете свой код в трех файлах:
describable.rs: Определите публичный типажDescribableс методомdescribe(&self) -> String.items.rs: Создайте две публичные структуры, которые реализуют ваш типаж:Bookс публичным полемtitle(String)Movieс публичным полемname(String)
Describableсо своим собственным уникальным форматом описания.main.rs: Объедините ваши модули, создайте экземпляры обеих структур и вызовите их методыdescribe, чтобы показать, как один и тот же типаж выдает разные результаты для разных типов.
Ваша структура Book должна описывать себя как Book: {title}, а ваша Movie должна описывать себя как Film: {name}. Обратите внимание, как каждый тип выполняет один и тот же контракт, но со своим собственным поведением!
В основном файле создайте книгу и фильм, используя два предоставленных входных значения, а затем выведите их описания на отдельных строках.
Ваш вывод должен соответствовать следующему формату:
Book: {title}
Film: {name}Например, при входных данных 1984 и Inception:
Book: 1984
Film: InceptionВы получите два входных значения: название книги и название фильма.
Шпаргалка
Чтобы реализовать типаж (trait) для структуры, используйте синтаксис impl TraitName for StructName:
trait Speak {
fn speak(&self) -> String;
}
struct Dog {
name: String,
}
impl Speak for Dog {
fn speak(&self) -> String {
format!("{} says: Woof!", self.name)
}
}
Несколько типов могут реализовывать один и тот же типаж с различным поведением:
struct Cat {
name: String,
}
impl Speak for Cat {
fn speak(&self) -> String {
format!("{} says: Meow!", self.name)
}
}
let dog = Dog { name: String::from("Rex") };
let cat = Cat { name: String::from("Whiskers") };
println!("{}", dog.speak()); // Rex says: Woof!
println!("{}", cat.speak()); // Whiskers says: Meow!
Это обеспечивает полиморфизм в Rust — когда разные типы используют один и тот же интерфейс с уникальными реализациями.
Попробуйте сами
mod describable;
mod items;
use describable::Describable;
use items::{Book, Movie};
fn main() {
// Чтение входных данных
let mut title = String::new();
std::io::stdin().read_line(&mut title).expect("Failed to read line");
let title = title.trim().to_string();
let mut name = String::new();
std::io::stdin().read_line(&mut name).expect("Failed to read line");
let name = name.trim().to_string();
// TODO: Создать экземпляр Book с названием title
// TODO: Создать экземпляр Movie с названием name
// TODO: Вывести описание книги
// TODO: Вывести описание фильма
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Object Oriented Programming
1Методы и поведение
Введение в блоки реализацииПараметр SelfИзменяемые методыАссоциированные функцииНесколько блоков реализацииЦепочки вызовов методовИтоги: действия с Rectangle4Проект: Виртуальный питомец
Определение питомцаКормление питомца2Инкапсуляция и модули
Основы модулейКлючевое слово PublicПриватные поляГеттерыСеттерыИтоги — Безопасный сейф5Обобщения
Обобщённые структурыОбобщённые методыНесколько обобщённых типовОбобщённые функцииПовторение — Координатная точка8Трейты как ограничения
Синтаксис ограничений трейтовМножественные ограниченияКонструкция whereВозврат типов с трейтамиИтоги — Обобщённый принтер11Паттерны проектирования в Rust
Паттерн NewtypeКомпозицияТрейт DropFrom и IntoИтоги — Мок-объект Smart Pointer3Продвинутые перечисления
Перечисления с даннымиМетоды в перечисленияхСопоставление вариантов с даннымиСнова об Option EnumИтоги — перечисление Shape6Определение трейтов
Что такое трейт?Реализация трейтовРеализации по умолчаниюПереопределение реализаций по умолчаниюТрейты с параметрамиИтоги — Media Player