rust - ¿Qué hace que algo sea un "objeto de rasgo"?
traits (2)
Tienes objetos de rasgo cuando tienes un puntero a un rasgo. Box
, Arc
, Rc
y la referencia &
son todos, en su núcleo, punteros. En términos de definir un "objeto de rasgo" funcionan de la misma manera.
Los "objetos de rasgo" son la toma de Rust en el despacho dinámico . Aquí hay un ejemplo que espero ayude a mostrar cuáles son los objetos de rasgo:
// 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 compilation)
fn static_dispatch<T>(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<dyn Bar> or
// Rc<dyn Bar> or Arc<dyn Bar>
//
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 Objetos de rasgos del libro de Rust
Los cambios recientes de Rust han hecho que los "objetos de rasgo" sean más prominentes para mí, pero solo tengo una nebulosa comprensión de lo que realmente convierte algo en un objeto de rasgo. Un cambio en particular es el próximo cambio para permitir que los objetos de rasgos reenvíen las implementaciones de rasgos al tipo interno.
Dado un rasgo Foo
, estoy bastante seguro de que Box<Foo>
es un objeto de rasgo. ¿Es &Foo
también un objeto de rasgo? ¿Qué pasa con otras cosas de punteros inteligentes como Rc
o Arc
? ¿Cómo podría hacer mi propio tipo que contaría como un objeto de rasgo?
La reference solo menciona objetos de rasgo una vez, pero nada como una definición.
Respuesta corta : Solo puede hacer rasgos seguros de objetos en objetos de rasgo.
Rasgos de seguridad de objetos : rasgos que no se resuelven en un tipo concreto de implementación. En la práctica, dos reglas gobiernan si un rasgo es seguro para objetos.
- El tipo de retorno no es uno mismo.
- No hay parámetros de tipo genérico.
Cualquier rasgo que satisfaga estas dos reglas se puede usar como objetos de rasgo.
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 utilizar como objeto de 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