Module 7 • Lesson 40

Dynamic Dispatch with dyn

📚 9 min💻 Free🦀 nixus.pro

Dynamic Dispatch with dyn Trait

Static dispatch (generics) is faster but creates code bloat. Dynamic dispatch (dyn Trait) is flexible but has a small runtime cost via virtual function tables (vtables).

trait Plugin: std::fmt::Debug {
    fn name(&self) -> &str;
    fn execute(&self, input: &str) -> String;
}

#[derive(Debug)]
struct UppercasePlugin;
#[derive(Debug)]
struct ReversePlugin;
#[derive(Debug)]
struct RepeatPlugin(usize);

impl Plugin for UppercasePlugin {
    fn name(&self) -> &str { "uppercase" }
    fn execute(&self, input: &str) -> String { input.to_uppercase() }
}
impl Plugin for ReversePlugin {
    fn name(&self) -> &str { "reverse" }
    fn execute(&self, input: &str) -> String { input.chars().rev().collect() }
}
impl Plugin for RepeatPlugin {
    fn name(&self) -> &str { "repeat" }
    fn execute(&self, input: &str) -> String { input.repeat(self.0) }
}

struct Pipeline { plugins: Vec> }

impl Pipeline {
    fn new() -> Self { Pipeline { plugins: Vec::new() } }
    fn add(mut self, p: Box) -> Self { self.plugins.push(p); self }
    fn run(&self, input: &str) -> String {
        self.plugins.iter().fold(input.to_string(), |s, p| p.execute(&s))
    }
}

fn main() {
    let pipeline = Pipeline::new()
        .add(Box::new(UppercasePlugin))
        .add(Box::new(RepeatPlugin(2)))
        .add(Box::new(ReversePlugin));

    println!("{}", pipeline.run("hello")); // OLLEHOLLEH
}

Object Safety

// A trait is object-safe if:
// 1. Methods do not use Self as a return type (except in specific ways)
// 2. No generic methods
// 3. No associated functions without self

// Object-safe:
trait Drawable { fn draw(&self); }

// NOT object-safe (generic method):
// trait Bad { fn compare(&self, other: T) -> bool; }

// Workaround: use associated types instead of generics in traits
trait Transformer {
    type Output;
    fn transform(&self, input: &str) -> Self::Output;
}

// Or use trait objects with Box for mixed types
fn main() {
    let items: Vec> = vec![];
    // Can hold any Drawable!
}

🎯 Practice

  1. Create an EventHandler trait and a Vec<Box<dyn EventHandler>> event system
  2. Implement a plugin registry: HashMap<String, Box<dyn Plugin>> where plugins can be looked up by name

🎉 Key Takeaways