¿Qué hace que algo sea un “objeto de rasgo”?

Los cambios recientes de Rust han hecho que los “objetos de rasgo” sean más prominentes para mí, pero solo tengo una comprensión nebulosa de lo que realmente convierte a algo en un objeto de rasgo. Un cambio en particular es el próximo cambio para permitir a los objetos de rasgo reenviar las implementaciones de rasgos al tipo interno.

Dado un rasgo Foo , estoy bastante seguro de que Box es un objeto de rasgo. Is &Foo también es un objeto de rasgo? ¿Qué pasa con otras cosas de puntero inteligente como Rc o Arc ? ¿Cómo podría hacer mi propio tipo que se consideraría un objeto rasgo?

La referencia solo menciona objetos de rasgo una vez, pero nada como una definición.

Tiene objetos de rasgo cuando tiene un puntero a un rasgo. Box , Arc , Rc y la referencia son todos ellos, en esencia, punteros. En términos de definir un “objeto de rasgo”, funcionan de la misma manera.

Los “objetos de rasgo” son la opinión de Rust sobre el despacho dynamic . Aquí hay un ejemplo que espero ayude a mostrar qué objetos de rasgo son:

 // define an example struct, make it printable #[derive(Debug)] struct Foo; // an example trait trait Bar { fn baz(&self); } // implement the trait for Foo impl Bar for Foo { fn baz(&self) { println!("{:?}", self) } } // This is a generic function that takes any T that implements trait Bar. // It must resolve to a specific concrete T at compile time. // The compiler creates a different version of this function // for each concrete type used to call it so &T here is NOT // a trait object (as T will represent a known, sized type // after comstacktion) fn static_dispatch(t: &T) where T:Bar { t.baz(); // we can do this because t implements Bar } // This function takes a pointer to a something that implements trait Bar // (it'll know what it is only at runtime). &dyn Bar is a trait object. // There's only one version of this function at runtime, so this // reduces the size of the compiled program if the function // is called with several different types vs using static_dispatch. // However performance is slightly lower, as the &dyn Bar that // dynamic_dispatch receives is a pointer to the object + // a vtable with all the Bar methods that the object implements. // Calling baz() on t means having to look it up in this vtable. fn dynamic_dispatch(t: &dyn Bar) { // ----------------^ // this is the trait object! It would also work with Box or // Rc or Arc // t.baz(); // we can do this because t implements Bar } fn main() { let foo = Foo; static_dispatch(&foo); dynamic_dispatch(&foo); } 

Para mayor referencia, hay un buen capítulo de Rasgos de objetos del libro de Rust

Respuesta corta : solo puede hacer rasgos seguros de objeto en objetos de rasgo.

Rasgos de objetos seguros : Rasgos que no se resuelven en un tipo concreto de implementación. En la práctica, dos reglas rigen si un rasgo es seguro para objetos.

  1. El tipo de devolución no es Self.
  2. No hay parámetros generics de tipo.

Cualquier rasgo que satisfaga estas dos reglas se puede usar como objetos de rasgo.

El ejemplo de rasgo que es seguro para objetos se puede usar como objeto de rasgo :

 trait Draw { fn draw(&self); } 

Ejemplo de rasgo que no se puede usar como objeto rasgo :

 trait Draw { fn draw(&self) -> Self; } 

Para una explicación detallada: https://doc.rust-lang.org/book/second-edition/ch17-02-trait-objects.html