0Pricing
Learn Rust Coding · Урок

Объекты трейтов и 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 — локальная установка не требуется.

Все уроки этого курса

  1. Определение трейтов
  2. Объекты трейтов и dyn
  3. Статическая и динамическая диспетчеризация
  4. Методы по умолчанию
← Назад к Learn Rust Coding