Trait objects
In Rust, traits are types, but they are "unsized", which roughly means that they are only allowed to show up behind a pointer like std::boxed::Box
⮳ (which points onto the heap) or &
(which can point anywhere).
A type like &dyn ClickCallback
or Box<dyn ClickCallback>
where ClickCallback
is a Trait, is called a "trait object", and includes a pointer to an instance of a type T
implementing ClickCallback
, and a 'vtable': a pointer to T
's implementation of each method in the trait.
trait Draw { fn draw(&self); } struct Button; impl Draw for Button { fn draw(&self) { println!("Button"); } } struct Text; impl Draw for Text { fn draw(&self) { println!("Text"); } } struct Screen { components: Vec<Box<dyn Draw>>, // <-- trait object } impl Screen { fn new() -> Self { Screen { components: vec![Box::new(Button), Box::new(Text), Box::new(Text)], } } fn run(&self) { for component in self.components.iter() { // The purpose of trait objects is to permit "late binding" of // methods. Calling a method on a trait object results // in virtual dispatch at runtime. Here, `components` is // a mix of `Button` and `Text` structs. component.draw(); } } } fn main() { let s = Screen::new(); s.run(); }
The set of traits after dyn
can be made up of an object-safe⮳ base trait plus any number of autotraits (one of std::marker::Send
⮳, std::marker::Sync
⮳, std::marker::Unpin
⮳, std::panic::UnwindSafe
⮳, and std::panic::RefUnwindSafe
⮳ - see special traits⮳).
dyn Trait
dyn Trait + Send
dyn Trait + Send + Sync
dyn Trait + 'static
See also
[trait_objects: review (P1)](https://github.com/john-cd/rust_howto/issues/562)