Objetos Trait

Los objetos de traits permiten valores de diferentes tipos, por ejemplo, en una colección:

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("The cat") // No name, cats won't respond to it anyway.
    }
}

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

Diseño de la memoria después de asignar pets:

name:Fido<Dog as Pet>::name<Cat as Pet>::nameStackHeappetsptrlen2capacity2
  • Los tipos que implementan un trait determinado pueden tener diferentes tamaños. Esto hace imposible tener elementos como Vec<Pet> en el ejemplo anterior.
  • dyn Pet es una forma de indicar al compilador un tipo de tamaño dinámico que implementa Pet.
  • En el ejemplo, pets contiene punteros grandes en los objetos que implementan Pet. El puntero grande consta de dos componentes: un puntero al objeto real y un puntero a la tabla de métodos virtuales para la implementación de Pet en ese objeto concreto.
  • Compara estas salidas en el ejemplo 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>>());