Cosa rende qualcosa un “object tratto”?

I recenti cambiamenti di rust hanno reso gli “oggetti tratti” più importanti per me, ma ho solo una comprensione nebulosa di ciò che rende veramente qualcosa in un object tratto. Un cambiamento in particolare è il cambiamento imminente per consentire agli oggetti tratto di inoltrare implementazioni di tratti al tipo interno.

Dato un tratto Foo , sono abbastanza sicuro che Box sia un object tratto. Is &Foo anche un object tratto? Che ne pensi di altre cose intelligenti come Rc o Arc ? Come potrei fare il mio tipo che valesse come object tratto?

Il riferimento menziona solo gli oggetti tratto una volta, ma nulla come una definizione.

Hai oggetti tratti quando hai un puntatore a un tratto. Box , Arc , Rc e il riferimento & sono tutti, al loro centro, puntatori. In termini di definizione di un “object tratto”, funzionano allo stesso modo.

Gli “oggetti tratti” sono quelli di Rust sull’invio dinamico . Ecco un esempio che spero aiuti a mostrare quali sono i tratti dei prodotti:

 // 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) 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); } 

Per ulteriori riferimenti, c’è un buon capitolo sui Trait Objects del libro Rust

Risposta breve : è ansible rendere i tratti sicuri degli oggetti solo in oggetti tratto.

Tratti Object-Safe : tratti che non si risolvono in concreto tipo di implementazione. In pratica, due regole governano se un tratto è sicuro per gli oggetti.

  1. Il tipo di ritorno non è il Sé.
  2. Non ci sono parametri di tipo generico.

Qualsiasi tratto che soddisfi queste due regole può essere usato come object tratto.

Esempio di tratto che è a prova di oggetti può essere usato come object tratto :

 trait Draw { fn draw(&self); } 

Esempio di tratto che non può essere utilizzato come object tratto :

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

Per una spiegazione dettagliata: https://doc.rust-lang.org/book/second-edition/ch17-02-trait-objects.html