트레잇 객체
트레잇 객체는 타입이 다른 값(예를 들어 컬렉션에 속한 각 값)들을 가질 수 있습니다:
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
를 할당한 이후의 메모리 레이아웃:
- 여러 타입이 같은 트레잇을 구현하더라도 그 크기는 서로 다를 수 있습니다. 그래서
Vec<Greeet>
같은 것은 불가능합니다. dyn Pet
이라고 하면이 타입의 크기는 동적이며Pet
을 구현하고 있다고 컴파일러에게 알려주는 것입니다.- 예제에서
pets
는Pet
을 구현하는 객체들의 _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>>());