ข้ามไปยังเนื้อหา

Trait Objects

ใน Rust มีสองวิธีในการเรียก method บน type ที่ implement trait:

  • Static dispatch (generics): compiler รู้ exact type ณ compile time และสร้าง code เฉพาะสำหรับแต่ละ type — ไม่มี runtime overhead
  • Dynamic dispatch (trait objects): type จริงถูกกำหนด ณ runtime ผ่าน pointer พิเศษที่เรียกว่า vtable

Trait object คือ &dyn Trait หรือ Box<dyn Trait> — ข้างในเก็บ pointer สองอัน: หนึ่งชี้ไปยังข้อมูล และอีกหนึ่งชี้ไปยัง vtable ที่เป็นตารางของ function pointer สำหรับ method ของ trait นั้น

dyn Trait โดยตัวเองมีขนาดที่ไม่แน่นอน ณ compile time (เนื่องจาก type จริงอาจมีขนาดต่างกัน) ดังนั้นคุณมักจะต้องวางไว้ใน Box ซึ่ง allocate บน heap:

let animals: Vec<Box<dyn Animal>> = vec![
Box::new(Dog),
Box::new(Cat),
];

นี่คือ pattern ทั่วไปสำหรับ heterogeneous collection — รายการที่อาจเก็บ type ต่างๆ ที่ implement trait เดียวกัน

// Static dispatch — compiler สร้าง code เฉพาะสำหรับ type ที่ส่งมา
fn notify(item: &impl Animal) { ... }
// Dynamic dispatch — ตัดสินใจ ณ runtime
fn notify(item: &dyn Animal) { ... }

ใช้ impl Trait เมื่อ type เป็นที่รู้จัก ณ compile time ใช้ dyn Trait เมื่อคุณต้องการความยืดหยุ่น ณ runtime เช่น เก็บ type ต่างๆ ใน Vec หรือส่งผ่าน closure กลับมาจาก function

Playground ด้านล่างแสดง trait Animal กับหลาย implementor และ Vec<Box<dyn Animal>> สังเกตว่า Dog และ Cat อยู่ใน Vec เดียวกันได้อย่างไร

trait Animal {
fn sound(&self) -> &str;
fn name(&self) -> &str;
}
struct Dog;
struct Cat;
impl Animal for Dog {
fn sound(&self) -> &str { "Woof" }
fn name(&self) -> &str { "Dog" }
}
impl Animal for Cat {
fn sound(&self) -> &str { "Meow" }
fn name(&self) -> &str { "Cat" }
}
fn make_sound(animal: &dyn Animal) {
println!("{} says: {}", animal.name(), animal.sound());
}
fn main() {
let animals: Vec<Box<dyn Animal>> = vec![
Box::new(Dog),
Box::new(Cat),
Box::new(Dog),
];
for animal in &animals {
make_sound(animal.as_ref());
}
}
`dyn Trait` ใน Rust หมายความว่าอะไร?
vtable คืออะไร?
ทำไม `dyn Trait` ต้องวางไว้หลัง pointer เช่น `Box<dyn Trait>` หรือ `&dyn Trait`?
แนวทางใดที่รองรับการเก็บค่าของ concrete type ต่างกันไว้ใน `Vec` เดียวกัน?