Трейты в Rust — это способ определения интерфейса: они описывают набор методов, который должен быть реализован типом. Реализация трейтa позволяет использовать генерики и динамическое вытеснение методов.
Статическая диспетчеризация (через дженерики и bound-ы):
Вызывается конкретная реализация метода во время компиляции. Используется обобщённый код (impl<T: Trait> или fn foo<T: Trait>(t: T)), во время компиляции для каждого типа создаётся отдельная версия функции (monomorphization).
Динамическая диспетчеризация:
Используется, когда функция принимает объекты как ссылки на трейт (&dyn Trait), компилятор не знает заранее, какой тип будет использован. Вызов метода происходит через vtable (виртуальную таблицу) во время выполнения.
Пример:
trait Animal { fn speak(&self); } struct Dog; impl Animal for Dog { fn speak(&self) { println!("Woof!"); } } // Статическая диспетчеризация def print_animal_static<T: Animal>(a: T) { a.speak(); } // Динамическая диспетчеризация def print_animal_dyn(a: &dyn Animal) { a.speak(); }
Когда использовать:
Можно ли хранить значения с дженериковым типом трейт в Vec<Trait>? Почему?
Ответ:
Нет, так сделать нельзя. Vec требует sized-типы, а трейты сами по себе не имеют размера. Нужно использовать Box<dyn Trait> или ссылки на трейты.
// let v: Vec<Trait> = vec![]; // ошибка компиляции let v: Vec<Box<dyn Animal>> = vec![Box::new(Dog)]; // корректно
История
Попытка создать коллекцию trait-объектов без использования Box или Rc:
// let coll: Vec<MyTrait> = vec![]; // ошибка: the size for values of type ... cannot be known
История
История
На демо клиенте пытались хранить ссылки на временные объекты (&dyn Trait) для взаимодействия с разными протоколами, ведущими к dangling references и runtime panic. Пришлось перейти на Box<dyn Trait> с явным владением.