¿Qué significa "dyn" en un tipo?

Resuelto Shepmaster asked hace 6 años • 3 respuestas

Recientemente vi código usando la dynpalabra clave:

fn foo(arg: &dyn Display) {}

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

¿Qué significa esta sintaxis?

Shepmaster avatar Jun 02 '18 02:06 Shepmaster
Aceptado

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 MyTraittipo 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 dynsintaxis. 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 Traitsintaxis y se esfuerza por hacer que el tipo de objeto de rasgo sea más claramente distinto de la sintaxis de rasgo "simple".

dynes 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 Traitsintaxis.

La sintaxis sin dynahora 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"?
Shepmaster avatar Jun 01 '2018 19:06 Shepmaster

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>);
spdrman avatar Mar 09 '2021 05:03 spdrman