Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Трейт-объекты

Syntax
TraitObjectTypedyn? TypeParamBounds

TraitObjectTypeOneBounddyn? TraitBound

Трейт-объект — это непрозрачное значение другого типа, которое реализует набор трейтов. Набор трейтов состоит из dyn-совместимого базового трейта плюс любое количество автотрейтов.

Трейт-объекты реализуют базовый трейт, его автотрейты и любые супертрейты базового трейта.

Трейт-объекты записываются как ключевое слово dyn, за которым следует набор ограничений трейтов, но со следующими ограничениями на ограничения трейтов.

Не может быть более одного не-автотрейта, не более одного времени жизни, и отключающие ограничения (например, ?Sized) не разрешены. Кроме того, пути к трейтам могут быть заключены в скобки.

Например, для трейта Trait следующие являются трейт-объектами:

  • dyn Trait
  • dyn Trait + Send
  • dyn Trait + Send + Sync
  • dyn Trait + 'static
  • dyn Trait + Send + 'static
  • dyn 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. Существуют умолчания, которые позволяют этому времени жизни обычно выводиться с разумным выбором.