트레잇 객체

트레잇 객체는 타입이 다른 값(예를 들어 컬렉션에 속한 각 값)들을 가질 수 있습니다:

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

pets를 할당한 이후의 메모리 레이아웃:

name:Fido<Dog as Pet>::name<Cat as Pet>::namepetsptrlen2capacity2
  • 여러 타입이 같은 트레잇을 구현하더라도 그 크기는 서로 다를 수 있습니다. 그래서 Vec<Greeet>같은 것은 불가능합니다.
  • dyn Pet이라고 하면이 타입의 크기는 동적이며 Pet을 구현하고 있다고 컴파일러에게 알려주는 것입니다.
  • 예제에서 petsPet을 구현하는 객체들의 _Fat 포인터_를 담고 있습니다. Fat 포인터는 실제 객체에 대한 포인터와 그 객체가 Pet을 구현하고 있는 가상 함수 테이블에 대한 포인터를 가집니다.
  • 아래 코드의 결과와 비교해보세요:
        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>>());