Pregunta ¿Cómo implementa Rust la reflexión?


Rust tiene la Any rasgo, pero también tiene una política de "no pagues por lo que no usas". ¿Cómo implementa Rust la reflexión?

Mi suposición es que Rust usa etiquetado perezoso. Todos los tipos no están asignados inicialmente, pero más adelante si se pasa una instancia del tipo a una función que espera un Any rasgo, el tipo se le asigna un TypeId.

O tal vez Rust pone una TypeId en cada tipo que su instancia posiblemente se pasa a esa función? Supongo que lo primero sería caro.


32
2018-04-05 03:46


origen


Respuestas:


En primer lugar, Rust no tiene reflejo; la reflexión implica que puede obtener detalles sobre un tipo en tiempo de ejecución, como los campos, los métodos, las interfaces que implementa, etc.  Tú no poder haz esto con Rust. Lo más cercano que puede obtener es implementar explícitamente (o derivar) un rasgo que proporciona esta información.

Cada tipo obtiene un TypeId asignado a él en tiempo de compilación. Porque tener identificaciones ordenadas globalmente es difícil, el ID es un entero derivado de una combinación de la definición del tipo y metadatos surtidos sobre la caja en la que está contenida. Para decirlo de otra manera: no están asignados en ningún tipo de orden, solo son hash de los diversos bits de información que entran en la definición del tipo. [1]

Si miras el fuente para el Any rasgo, verá la implementación única para Any:

impl<T: 'static + ?Sized > Any for T {
    fn get_type_id(&self) -> TypeId { TypeId::of::<T>() }
}

(Los límites pueden ser informalmente reducido a "todos los tipos que no son prestados de otra cosa").

También puedes encontrar la definición de TypeId:

pub struct TypeId {
    t: u64,
}

impl TypeId {
    pub const fn of<T: ?Sized + 'static>() -> TypeId {
        TypeId {
            t: unsafe { intrinsics::type_id::<T>() },
        }
    }
}

intrinsics::type_id es una función interna reconocida por el compilador que, dado un tipo, devuelve su ID de tipo interno. Esta llamada simplemente se reemplaza en tiempo de compilación con el ID de tipo entero literal; no hay real llama aquí. [2] Así es como TypeId sabe lo que es la identificación de un tipo. TypeId, entonces, es solo una envoltura alrededor de esto u64 para ocultar los detalles de implementación de los usuarios. Si lo encuentras conceptualmente más simple, puedes pensar en un tipo TypeId como un entero constante de 64 bits que el compilador acaba de sabe en tiempo de compilación

Any reenvía a esto desde get_type_id, significa que get_type_id es De Verdad simplemente vinculando el método de rasgo al apropiado TypeId::of método. Solo está ahí para garantizar que si tienes un Any, puedes encontrar el tipo original TypeId.

Ahora, Any se implementa para más tipos, pero esto no significa que todos esos tipos en realidad tienen un Any implementación flotando en la memoria. Lo que sucede en realidad es que el compilador solo genera el código real para un tipo Any implementación si alguien escribe el código que lo requiere. [3] En otras palabras, si nunca usas el Any implementación para un tipo dado, el compilador nunca lo generará.

Así es como Rust cumple "no pagues por lo que no usas": si nunca pasas un tipo dado como &Anyo Box<Any>, entonces el código asociado nunca se genera y nunca ocupa espacio en el binario compilado.


[1]: Frustrante, esto significa que un tipo TypeId poder valor de cambio dependiendo de precisión cómo la biblioteca se compila, hasta el punto de que compilarla como una dependencia (a diferencia de una compilación independiente) causa TypeIds para cambiar

[2]: En la medida en que soy consciente. yo podría estar equivocado acerca de esto, pero yo estaría De Verdad sorprendido si ese es el caso.

[3]: Esto es en general cierto de los genéricos en Rust.


43
2018-04-05 04:09