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.
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).
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?
AlwaysWhen one collection holds values of different types sharing a traitFor faster codeWhen the trait has no methods
Show answer
Answer: When one collection holds values of different types sharing a trait