Объекты трейтов и dyn
Динамическая диспетчеризация
«Объекты трейтов и dyn» — бесплатный урок Learn Rust Coding на CoddyKit. Это урок 2 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Learn Rust Coding, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Learn Rust Coding содержит 4 уроков всего.
Проблема: смешанные типы
Обобщённые типы с ограничениями трейтов отлично работают, когда каждый вызов использует один конкретный тип. Но что, если нужна коллекция разных типов, у которых есть общий трейт? Обобщённые типы не могут хранить такую смесь.
Объекты трейтов решают эту проблему.
Что такое объект трейта
Объект трейта — это значение, к которому обращаются через указатель и ключевое слово dyn, например &dyn Trait или Box<dyn Trait>.
Он позволяет одинаково работать с разными конкретными типами во время выполнения.
Коробка с dyn Trait
Box<dyn Trait> владеет значением в куче, имеющим некоторый тип, реализующий трейт. Вы вызываете методы трейта, не зная конкретного типа.
trait Speak { fn say(&self) -> String; }
struct Dog;
impl Speak for Dog { fn say(&self) -> String { String::from("Woof") } }
fn main() {
let animal: Box<dyn Speak> = Box::new(Dog);
println!("{}", animal.say());
}Vec объектов трейтов
Главное преимущество: один вектор может содержать множество разных типов, если каждый из них реализует трейт.
trait Speak { fn say(&self) -> String; }
struct Dog;
struct Cat;
impl Speak for Dog { fn say(&self) -> String { String::from("Woof") } }
impl Speak for Cat { fn say(&self) -> String { String::from("Meow") } }
fn main() {
let zoo: Vec<Box<dyn Speak>> = vec![Box::new(Dog), Box::new(Cat)];
for animal in &zoo {
println!("{}", animal.say());
}
}Динамическая диспетчеризация
Для объектов трейтов вызываемый метод выбирается во время выполнения поиском в скрытой таблице — таблице виртуальных методов (vtable). Это называется динамической диспетчеризацией.
Цена — небольшое косвенное обращение; преимущество — гибкость во время выполнения.
Функции, возвращающие объекты трейтов
Функция может возвращать Box<dyn Trait>, если конкретный тип меняется. Это полезно для фабричных функций, которые выбирают тип во время выполнения.
trait Speak { fn say(&self) -> String; }
struct Dog;
struct Cat;
impl Speak for Dog { fn say(&self) -> String { String::from("Woof") } }
impl Speak for Cat { fn say(&self) -> String { String::from("Meow") } }
fn make(kind: &str) -> Box<dyn Speak> {
if kind == "dog" { Box::new(Dog) } else { Box::new(Cat) }
}
fn main() {
println!("{}", make("cat").say());
}Заимствованные объекты трейтов
Также можно передавать заимствованный объект трейта через &dyn Trait, если владение не требуется. Выделение памяти в куче не происходит.
trait Speak { fn say(&self) -> String; }
struct Dog;
impl Speak for Dog { fn say(&self) -> String { String::from("Woof") } }
fn announce(s: &dyn Speak) {
println!("heard: {}", s.say());
}
fn main() {
let d = Dog;
announce(&d);
}Безопасность объектов
Не каждый трейт может быть объектом трейта. Трейт должен быть безопасным для объектов: в общих чертах, его методы не должны возвращать Self по значению или использовать обобщённые параметры типов.
Методы, принимающие &self и использующие конкретные типы, подходят.
Пример небезопасного для объектов трейта
Трейт с методом, возвращающим Self, нельзя использовать как dyn, потому что размер Self неизвестен за указателем. Делайте трейты-объекты простыми.
Пример в стиле плагинов
Объекты трейтов особенно полезны в системах плагинов: храните список обработчиков за dyn и запускайте их все, не заботясь о конкретных типах.
trait Task { fn run(&self) -> i32; }
struct Add { a: i32, b: i32 }
struct Negate { x: i32 }
impl Task for Add { fn run(&self) -> i32 { self.a + self.b } }
impl Task for Negate { fn run(&self) -> i32 { -self.x } }
fn main() {
let tasks: Vec<Box<dyn Task>> = vec![Box::new(Add { a: 2, b: 3 }), Box::new(Negate { x: 7 })];
let total: i32 = tasks.iter().map(|t| t.run()).sum();
println!("total {}", total);
}Когда использовать dyn
Используйте объекты трейтов, когда нужны разнородные коллекции, типы, выбираемые во время выполнения, или уменьшение разрастания кода из-за множества подстановок обобщённых типов.
В остальных случаях предпочитайте обобщённые типы для максимальной скорости.
Быстрая проверка
Проверьте, насколько хорошо вы понимаете объекты трейтов.
Итоги
Вы изучили динамическую диспетчеризацию:
- Объекты трейтов используют
dynза указателем (Box<dyn T>,&dyn T) - Они позволяют создавать разнородные коллекции и использовать типы, выбранные во время выполнения
- Вызовы методов проходят через таблицу виртуальных методов (динамическая диспетчеризация)
- Для такого использования трейты должны быть безопасными для объектов
Часто задаваемые вопросы
Урок «Объекты трейтов и dyn» бесплатный?
Да — полный текст урока «Объекты трейтов и dyn» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Learn Rust Coding, подпишись на CoddyKit PRO. Курс Learn Rust Coding содержит 4 уроков всего.
Чему я научусь в уроке «Объекты трейтов и dyn»?
Динамическая диспетчеризация Ты практикуешь Learn Rust Coding с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать Learn Rust Coding?
Предыдущий опыт не требуется. Learn Rust Coding на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 2 из 4.
Сколько времени занимает урок «Объекты трейтов и dyn»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке Learn Rust Coding?
Да. Каждый урок Learn Rust Coding включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Определение трейтов
- Объекты трейтов и dyn
- Статическая и динамическая диспетчеризация
- Методы по умолчанию