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.
/// A trait for types that can be drawn. trait Draw { /// Draws the object. fn draw(&self); } /// A struct representing a button. struct Button; /// Dummy implementation of the trait for `Button`. impl Draw for Button { fn draw(&self) { println!("Button"); } } /// A struct representing text. struct Text; impl Draw for Text { fn draw(&self) { println!("Text"); } } /// A struct representing a screen that can display multiple components. struct Screen { /// A vector of trait objects that can be drawn. /// Note the `dyn` keyword. /// We use a trait object, because the `Screen` may hold a mix of Button /// and Text objects, which may be unknown until run-time. /// A generic type would not work here - it would allow only one type or /// the other, not both. Trait objects are dynamically sized types. /// Like all DSTs, trait objects must be used behind some type of pointer. components: Vec<Box<dyn Draw>>, } impl Screen { /// Creates a new screen with some components. fn new() -> Self { Screen { components: vec![Box::new(Button), Box::new(Text), Box::new(Text)], } } /// Runs the screen, drawing each component. 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. 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