러스트의 트레이트는 인터페이스를 정의하는 방식입니다: 트레이트는 타입이 구현해야 하는 메서드 집합을 설명합니다. 트레이트의 구현은 제네릭과 메서드의 동적 디스패치를 사용할 수 있게 해줍니다.
정적 디스패치 (제네릭 및 바운드를 통한):
컴파일 시간에 특정 메서드 구현이 호출됩니다. 제네릭 코드(impl<T: Trait> 또는 fn foo<T: Trait>(t: T))가 사용되며, 컴파일 중 각 타입에 대해 별도의 함수 버전이 생성됩니다(모노모르피제이션).
동적 디스패치:
함수가 트레이트에 대한 객체를 참조로 받을 때(&dyn Trait), 컴파일러는 어떤 타입이 사용될지 미리 알 수 없습니다. 메서드 호출은 실행 시간에 vtable(가상 테이블)을 통해 이루어집니다.
예제:
trait Animal { fn speak(&self); } struct Dog; impl Animal for Dog { fn speak(&self) { println!("Woof!"); } } // 정적 디스패치 def print_animal_static<T: Animal>(a: T) { a.speak(); } // 동적 디스패치 def print_animal_dyn(a: &dyn Animal) { a.speak(); }
사용 시기:
제네릭 타입 트레이트를 Vec<Trait>에 저장할 수 있나요? 그 이유는 무엇인가요?
답변:
아니요, 그렇게 할 수 없습니다. Vec는 사이즈가 있는 타입을 요구하는데, 트레이트는 본질적으로 크기가 없습니다. Box<dyn Trait> 또는 트레이트에 대한 참조를 사용해야 합니다.
// let v: Vec<Trait> = vec![]; // 컴파일 오류 let v: Vec<Box<dyn Animal>> = vec![Box::new(Dog)]; // 정상
이야기
Box 또는 Rc를 사용하지 않고 트레이트 객체 컬렉션을 만들려는 시도:
// let coll: Vec<MyTrait> = vec![]; // 오류: the size for values of type ... cannot be known
이야기
이야기
데모 클라이언트에서 다양한 프로토콜과 상호 작용하기 위해 임시 객체에 대한 참조(&dyn Trait)를 저장하려고 시도했으나, 이로 인해 dangling references와 runtime panic이 발생했습니다. 명시적인 소유권을 가진 Box<dyn Trait>로 변경해야 했습니다.