Trait Objects
Static Dispatch vs Dynamic Dispatch
หัวข้อที่มีชื่อว่า “Static Dispatch vs Dynamic Dispatch”ใน 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 Objects คืออะไร
หัวข้อที่มีชื่อว่า “Trait Objects คืออะไร”Trait object คือ &dyn Trait หรือ Box<dyn Trait> — ข้างในเก็บ pointer สองอัน: หนึ่งชี้ไปยังข้อมูล และอีกหนึ่งชี้ไปยัง vtable ที่เป็นตารางของ function pointer สำหรับ method ของ trait นั้น
เมื่อใดที่ใช้ Box<dyn Trait>
หัวข้อที่มีชื่อว่า “เมื่อใดที่ใช้ Box<dyn 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 เดียวกัน
impl Trait vs dyn Trait
หัวข้อที่มีชื่อว่า “impl Trait vs dyn Trait”// Static dispatch — compiler สร้าง code เฉพาะสำหรับ type ที่ส่งมาfn notify(item: &impl Animal) { ... }
// Dynamic dispatch — ตัดสินใจ ณ runtimefn 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()); }}Compiling…