O que faz de algo um “object de traço”?

Mudanças recentes em rust tornaram os “objects de traço” mais proeminentes para mim, mas eu só tenho uma compreensão nebulosa do que realmente transforma algo em um object de característica. Uma mudança em particular é a próxima mudança para permitir que os objects de traço redirecionem as implementações de traço para o tipo interno.

Dado um traço Foo , eu tenho certeza que Box é um object de traço. O &Foo também é um object de traço? E quanto a outras coisas de ponteiro inteligente como Rc ou Arc ? Como eu poderia fazer o meu próprio tipo que contaria como um object de traço?

A referência menciona apenas objects de traço uma vez, mas nada como uma definição.

Você tem objects de traço quando você tem um ponteiro para uma característica. Box , Arc , Rc e a referência & são todos, no seu núcleo, pointers. Em termos de definir um “object de traço”, eles funcionam da mesma maneira.

“Objetos com traços” são os dados de Rust sobre o despacho dynamic . Aqui está um exemplo que espero que ajude a mostrar quais objects de traits são:

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

Para referência futura, há um bom capítulo de Objetos de Traço do livro Rust

Resposta Curta : Você só pode fazer traços seguros para objects em objects de traços.

Características Seguras para Objetos : Características que não resolvem o tipo concreto de implementação. Na prática, duas regras governam se uma característica é segura para objects.

  1. O tipo de retorno não é Self.
  2. Não há parâmetros de tipo genérico.

Qualquer traço que satisfaça essas duas regras pode ser usado como object de traço.

Exemplo de traço que é object seguro pode ser usado como object de traço :

 trait Draw { fn draw(&self); } 

Exemplo de traço que não pode ser usado como object de traço :

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

Para uma explicação detalhada: https://doc.rust-lang.org/book/second-edition/ch17-02-trait-objects.html