Что означает "дин" в типе?
Я недавно видел код, использующий 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
- и другие типы указателей
Переменные/параметры, содержащие трейт-объекты, представляют собой толстые указатели, состоящие из следующих компонентов:
- указатель на объект в памяти
- указатель на виртуальную таблицу этого объекта, виртуальная таблица - это таблица с указателями, которые указывают на фактическую реализацию (ы) метода (ов).
См. мой ответ о том, что делает что-то «чертовым объектом»?для получения дополнительной информации.