Что означает "дин" в типе?

Я недавно видел код, использующий dyn ключевое слово:

fn foo(arg: &dyn Display) {}

fn bar() -> Box<dyn Display> {}

Что означает этот синтаксис?

3 ответа

TL;DR: это синтаксис для указания типа объекта черты, который должен быть предпочтительным для ясности.


Начиная с Rust 1.0 черты ведут двойную жизнь. После того, как признак был объявлен, он может использоваться как признак или как тип:

// As a trait
impl MyTrait for SomeType {}

// As a type!
impl MyTrait {}
impl AnotherTrait for MyTrait {}

Как вы можете себе представить, это двойное значение может вызвать некоторую путаницу. Кроме того, так как MyTrait тип - это тип без динамического размера, который может подвергать людей очень сложным сообщениям об ошибках.

Чтобы смягчить эту проблему, RFC 2113 представил dyn синтаксис. Этот синтаксис доступен начиная с Rust 1.27:

use std::{fmt::Display, sync::Arc};

fn main() {
    let display_ref: &dyn Display = &42;
    let display_box: Box<dyn Display> = Box::new(42);
    let display_arc: Arc<dyn Display> = Arc::new(42);
}

Это новое ключевое слово параллельно impl Trait синтаксис и стремится сделать тип объекта признака более явно отличным от "голого" синтаксиса признака.

Вполне вероятно, что в следующей редакции Rust чистый синтаксис будет устарел, а затем в конечном итоге удален.

Я нашел этот пост в блоге, чтобы очень четко объяснить эту функцию: https://medium.com/digitalfrontiers/rust-dynamic-dispatching-deep-dive-236a5896e49b .

Соответствующий отрывок:

      struct Service<T:Backend>{
    backend: Vec<T>  // Either Vec<TypeA> or Vec<TypeB>, not both
}
...
let mut backends = Vec::new();
backends.push(TypeA);
backends.push(TypeB);  // <---- Type error here

против

      struct Service{
    backends: Vec<Box<dyn Backend>>
}
...
let mut backends = Vec::new();
backends.push( Box::new(PositiveBackend{}) as Box<dyn Backend>);
backends.push( Box::new(NegativeBackend{}) as Box<dyn Backend>);

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

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

Другими словами, мы не знаем конкретный тип объекта во время компиляции, мы просто знаем, что объект реализует трейт.

Поскольку размер типаж-объекта неизвестен во время компиляции, он должен быть помещен за указателем. Например, если Traitимя вашего трейта, то вы можете использовать свои трейт-объекты следующим образом:

  • Box<dyn Trait>
  • &dyn Trait
  • и другие типы указателей

Переменные/параметры, содержащие трейт-объекты, представляют собой толстые указатели, состоящие из следующих компонентов:

  • указатель на объект в памяти
  • указатель на виртуальную таблицу этого объекта, виртуальная таблица - это таблица с указателями, которые указывают на фактическую реализацию (ы) метода (ов).

См. мой ответ о том, что делает что-то «чертовым объектом»?для получения дополнительной информации.

Другие вопросы по тегам