Box<dyn Trait>
인 Trait 객체를 만들기 위해서는 객체 안전성을 지켜야 한다.
즉, 트레이트 메서드가 반드시 self
매개변수를 가져야 한다는 중요한 규칙이 있다. 이 규칙을 지켜야만 vtable
을 통해 올바른 메서드 호출을 동적으로 디스패치할 수 있다.
trait Drawable {
fn draw();
}
#[derive(Default)]
struct Button;
impl Drawable for Button {
fn draw() {
println!("Button draw!");
}
}
#[derive(Default)]
struct Toggle;
impl Drawable for Toggle {
fn draw() {
println!("Toggle draw!");
}
}
fn main() {
let drawable_list: Vec<Box<dyn Drawable>> = Vec::new();
}
위와 같은 코드가 있다. trait
인 Drawable
에 draw
라는 연관 함수를 만들었다.
Button
과 Toggle
구조체를 만들어 각각 Drawable
을 구현했다. main
에서 사용하려고 하는데 오류가 발생하였다.
이 에러 메시지의 의미는 해당 트레이트가 동적 디스패치
에 적합하지 않다는 의미이다. 이것은 객체 안전성
규칙을 위반했기 때문에 발생한다.
트레이트 객체의 경우 동적으로 생성 시 vtable
을 가리키는 포인터를 가진다. 이때 트레이트 메서드가 self
를 가지고 있지 않다면 vtable
에 포함될 수 없기 때문에 트레이트 객체를 만들 수 없다.
trait Drawable {
fn draw(&self);
}
#[derive(Default)]
struct Button;
impl Drawable for Button {
fn draw(&self) {
println!("Button draw!");
}
}
#[derive(Default)]
struct Toggle;
impl Drawable for Toggle {
fn draw(&self) {
println!("Toggle draw!");
}
}
fn main() {
let drawable_list: Vec<Box<dyn Drawable>> = vec![Box::new(Button::default()), Box::new(Toggle::default())];
for draw_obj in drawable_list {
draw_obj.draw();
}
}
정상적으로 출력이 되는 것을 볼 수 있다.
main
함수가 호출이 되면서 런타임에 동적으로 트레이트 객체가 생성이 된다.
rust를 사용하여 개발하며 Box<dyn >
으로 동적 디스패치를 하기는 했지만 정확히 어떻게 동작하는지 몰랐는데 이번 기회를 통해서 좀 더 깊이 있게 알게 된 것 같다.