¿Qué significa "dyn" en un tipo?
Recientemente vi código usando la dyn
palabra clave:
fn foo(arg: &dyn Display) {}
fn bar() -> Box<dyn Display> {}
¿Qué significa esta sintaxis?
TL;DR: Es una sintaxis para especificar el tipo de un objeto de rasgo y debe especificarse por razones de claridad.
Desde Rust 1.0, los rasgos han tenido una doble vida. Una vez que se ha declarado un rasgo, se puede utilizar como rasgo o como tipo:
// As a trait
impl MyTrait for SomeType {}
// As a type!
impl MyTrait {}
impl AnotherTrait for MyTrait {}
Como puedes imaginar, este doble significado puede causar cierta confusión. Además, dado que el MyTrait
tipo es sin tamaño o de tamaño dinámico, esto puede exponer a las personas a mensajes de error muy complejos.
Para solucionar este problema, RFC 2113 introdujo la dyn
sintaxis. Esta sintaxis está disponible a partir de 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);
}
Esta nueva palabra clave es paralela a la impl Trait
sintaxis y se esfuerza por hacer que el tipo de objeto de rasgo sea más claramente distinto de la sintaxis de rasgo "simple".
dyn
es la abreviatura de "dinámico" y se refiere al hecho de que los objetos de rasgo realizan un envío dinámico . Esto significa que la decisión de exactamente qué función se llama se producirá en el tiempo de ejecución del programa. Compare esto con el envío estático que utiliza la impl Trait
sintaxis.
La sintaxis sin dyn
ahora está obsoleta y se eliminó en la edición de 2021 .
- ¿Por qué debería implementar métodos en un rasgo en lugar de como parte del rasgo?
- ¿Qué hace que algo sea un "objeto rasgo"?
TLDR: "dyn" le permite almacenar en una Caja una combinación de Manzanas y Naranjas, porque todas implementan el mismo rasgo de Fruta, que es lo que su Caja usa como restricción de tipo, en lugar de solo un tipo genérico. Esto se debe a que Generic permite UNO de Apple O Orange, pero no ambos:
Vec<Box<T>> --> Vector can hold boxes of either Apples OR Oranges structs
Vec<Box<dyn Fruit>> --> Vector can now hold a mix of boxes of Apples AND Oranges Structs
Si desea almacenar varios tipos en la misma instancia de una estructura de datos, debe usar un rasgo que envuelva un tipo genérico y etiquetarlo como "dyn", lo que hará que ese tipo genérico se resuelva cada vez que se llame. , durante el tiempo de ejecución.
A veces, en lugar de utilizar un tipo (String, &str, i32, etc...) o genérico (T, Vec, etc...), utilizamos un rasgo como restricción de tipo (es decir, TryFrom). Esto es para permitirnos almacenar múltiples tipos (todos implementando el rasgo requerido), en la misma instancia de estructura de datos (probablemente también necesitará Box<>).
"dyn" básicamente le dice al compilador que no sabemos cuál será el tipo en tiempo de compilación en lugar del rasgo, y que se determinará en tiempo de ejecución. Esto permite que el tipo final sea en realidad una mezcla de tipos que implementan el rasgo.
Para los genéricos, el compilador codificará el tipo en lugar de nuestro tipo genérico en el primer uso de la llamada a nuestra estructura de datos que consume los genéricos. Se espera que todas las demás llamadas para almacenar datos en esa misma estructura de datos utilicen el mismo tipo que en la primera llamada.
ADVERTENCIA Como ocurre con todas las cosas, existe una penalización de rendimiento por implementar flexibilidad adicional, y este caso definitivamente tiene una penalización de rendimiento.
Encontré esta publicación de blog para explicar esta característica muy claramente: https://medium.com/digitalfrontiers/rust-dynamic-dispatching-deep-dive-236a5896e49b
Extracto relevante:
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
vs
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>);