Objetos Trait

Objetos trait permitem valores de diferentes tipos, por exemplo, em uma coleção:

trait Pet {
    fn name(&self) -> String;
}

struct Dog {
    name: String,
}

struct Cat;

impl Pet for Dog {
    fn name(&self) -> String {
        self.name.clone()
    }
}

impl Pet for Cat {
    fn name(&self) -> String {
        String::from("Gato") // Sem nomes, gatos não respondem mesmo.
    }
}

fn main() {
    let pets: Vec<Box<dyn Pet>> = vec![
        Box::new(Cat),
        Box::new(Dog { name: String::from("Bidu") }),
    ];
    for pet in pets {
        println!("Olá {}!", pet.name());
    }
}

Layout da memória após alocar pets:

nome:Bidu<Cachorro as Pet>::nome<Gato as Pet>::nomePilhaHeappetsptrtamanho2capac.2
  • Tipos que implementam um dado trait podem ter tamanhos diferentes. Isto torna impossível haver coisas como Vec<Pet> no exemplo anterior.
  • dyn Pet é uma maneira de dizer ao compilador sobre um tipo de tamanho dinâmico que implementa Pet.
  • No exemplo, pets possui fat pointers para objetos que implementam Pet. O fat pointer consiste em dois componentes, um ponteiro para o objeto propriamente dito e um ponteiro para a tabela de métodos virtuais para a implementação de Pet do objeto em particular.
  • Compare estas saídas no exemplo anterior::
        println!("{} {}", std::mem::size_of::<Dog>(), std::mem::size_of::<Cat>());
        println!("{} {}", std::mem::size_of::<&Dog>(), std::mem::size_of::<&Cat>());
        println!("{}", std::mem::size_of::<&dyn Pet>());
        println!("{}", std::mem::size_of::<Box<dyn Pet>>());