Трейт-объекты
Syntax
TraitObjectType → dyn? TypeParamBounds
Трейт-объект — это непрозрачное значение другого типа, которое реализует набор трейтов. Набор трейтов состоит из dyn-совместимого базового трейта плюс любое количество автотрейтов.
Трейт-объекты реализуют базовый трейт, его автотрейты и любые супертрейты базового трейта.
Трейт-объекты записываются как ключевое слово dyn, за которым следует набор ограничений трейтов,
но со следующими ограничениями на ограничения трейтов.
Не может быть более одного не-автотрейта, не более одного
времени жизни, и отключающие ограничения (например, ?Sized) не разрешены. Кроме того,
пути к трейтам могут быть заключены в скобки.
Например, для трейта Trait следующие являются трейт-объектами:
dyn Traitdyn Trait + Senddyn Trait + Send + Syncdyn Trait + 'staticdyn Trait + Send + 'staticdyn Trait +dyn 'static + Trait.dyn (Trait)
2021 Edition differences
До редакции 2021 года ключевое слово
dynмогло быть опущено.
2018 Edition differences
В редакции 2015, если первое ограничение трейт-объекта — это путь, который начинается с
::, тоdynбудет рассматриваться как часть пути. Первый путь можно заключить в скобки, чтобы обойти это. Таким образом, если вы хотите трейт-объект с трейтом::your_module::Trait, вы должны записать его какdyn (::your_module::Trait).Начиная с редакции 2018,
dynявляется настоящим ключевым словом и не разрешен в путях, поэтому скобки не нужны.
Два типа трейт-объектов являются псевдонимами друг друга, если базовые трейты являются псевдонимами друг друга и
если наборы автотрейтов одинаковы и границы времени жизни одинаковы.
Например, dyn Trait + Send + UnwindSafe совпадает с
dyn Trait + UnwindSafe + Send.
Из-за непрозрачности того, какого конкретного типа значение, трейт-объекты являются
динамически sized типами. Как и все
DST, трейт-объекты используются
за каким-то типом указателя; например, &dyn SomeTrait или
Box<dyn SomeTrait>. Каждый экземпляр указателя на трейт-объект включает:
- указатель на экземпляр типа
T, который реализуетSomeTrait - таблицу виртуальных методов, часто называемую vtable, которая содержит для
каждого метода
SomeTraitи его супертрейтов, которыеTреализует, указатель на реализациюT(т.е. указатель на функцию).
Цель трейт-объектов — разрешить “позднее связывание” методов. Вызов метода на трейт-объекте приводит к виртуальной диспетчеризации во время выполнения: то есть указатель на функцию загружается из vtable трейт-объекта и вызывается косвенно. Фактическая реализация для каждой записи vtable может различаться для каждого объекта.
Пример трейт-объекта:
trait Printable { fn stringify(&self) -> String; } impl Printable for i32 { fn stringify(&self) -> String { self.to_string() } } fn print(a: Box<dyn Printable>) { println!("{}", a.stringify()); } fn main() { print(Box::new(10) as Box<dyn Printable>); }
В этом примере трейт Printable встречается как трейт-объект в
сигнатуре типа print и в выражении приведения в main.
Границы времени жизни трейт-объекта
Поскольку трейт-объект может содержать ссылки, время жизни этих ссылок
нужно выразить как часть трейт-объекта. Это время жизни записывается как
Trait + 'a. Существуют умолчания, которые позволяют этому времени жизни обычно
выводиться с разумным выбором.