Rust Tutorial · Traits and Generics · Lesson 22 of 29

Rust Trait Objects and dyn

Store values of different types behind one interface with Box<dyn Trait>, and when to choose it over generics.

Create a free account to save this page, take notes and track your progress. Sign in

dyn Trait

Generics need the type at compile time. When a collection holds different types that share a trait, use a trait object: Box<dyn Trait> or &dyn Trait. Calls go through a vtable (dynamic dispatch).

A list of different notifiers
trait Notifier {
    fn send(&self, message: &str) -> String;
}

struct Email { to: String }
struct Sms { number: String }

impl Notifier for Email {
    fn send(&self, message: &str) -> String { format!("email to {}: {message}", self.to) }
}
impl Notifier for Sms {
    fn send(&self, message: &str) -> String { format!("sms to {}: {message}", self.number) }
}

fn main() {
    let channels: Vec<Box<dyn Notifier>> = vec![
        Box::new(Email { to: "[email protected]".into() }),
        Box::new(Sms { number: "+49 151 0000".into() }),
    ];
    for channel in &channels {
        println!("{}", channel.send("your order shipped"));
    }
}

Output

email to [email protected]: your order shipped
sms to +49 151 0000: your order shipped

Run this on your computer — see “Get Started” for setup.

Choose generics for performance and when types are known at compile time; choose trait objects for heterogeneous collections and plug-in style code.

Test yourself

When do you need Box<dyn Trait> instead of generics?

  1. Always
  2. When one collection holds values of different types sharing a trait
  3. For faster code
  4. When the trait has no methods
Show answer

Answer: When one collection holds values of different types sharing a trait

Was this page helpful?