Trait objects let you use different types in the same place if they share some behavior. This helps write flexible and reusable code.
Trait objects overview in Rust
let obj: &dyn TraitName = &value;
let boxed_obj: Box<dyn TraitName> = Box::new(value);dyn TraitName means a trait object for the trait TraitName.
You can use references &dyn TraitName or smart pointers like Box<dyn TraitName>.
trait Speak {
fn speak(&self);
}
struct Dog;
impl Speak for Dog {
fn speak(&self) {
println!("Woof!");
}
}
fn main() {
let dog = Dog;
let speaker: &dyn Speak = &dog;
speaker.speak();
}trait Draw {
fn draw(&self);
}
struct Circle;
impl Draw for Circle {
fn draw(&self) {
println!("Drawing a circle");
}
}
fn main() {
let shapes: Vec<Box<dyn Draw>> = vec![Box::new(Circle)];
for shape in shapes {
shape.draw();
}
}This program shows how to use trait objects to call the same method on different types that implement the Animal trait.
trait Animal {
fn sound(&self);
}
struct Cat;
impl Animal for Cat {
fn sound(&self) {
println!("Meow");
}
}
struct Dog;
impl Animal for Dog {
fn sound(&self) {
println!("Woof");
}
}
fn make_sound(animal: &dyn Animal) {
animal.sound();
}
fn main() {
let cat = Cat;
let dog = Dog;
let animals: Vec<&dyn Animal> = vec![&cat, &dog];
for animal in animals {
make_sound(animal);
}
}Trait objects use dynamic dispatch, which means the method to call is decided at runtime.
You cannot use trait objects with traits that have generic methods or associated constants.
Use dyn keyword to clearly mark trait objects since Rust 2018 edition.
Trait objects let you work with different types through a common interface.
They enable dynamic dispatch to call methods at runtime.
Use references or smart pointers with dyn Trait to create trait objects.
Practice
trait objects in Rust?Solution
Step 1: Understand trait objects concept
Trait objects let you use different types that implement the same trait through a common interface.Step 2: Identify dynamic dispatch role
Trait objects enable dynamic dispatch, meaning method calls are decided at runtime, allowing flexibility.Final Answer:
To allow different types to be used through a common interface at runtime -> Option AQuick Check:
Trait objects = common interface + runtime dispatch [OK]
- Confusing trait objects with static dispatch
- Thinking trait objects create new unrelated types
- Believing trait objects improve compile-time speed
Animal?Solution
Step 1: Recall trait object syntax
Trait objects require thedynkeyword before the trait name when used as a reference or pointer.Step 2: Check each option
let a: &dyn Animal = &dog; uses&dyn Animal, which is the correct syntax for a trait object reference.Final Answer:
let a: &dyn Animal = &dog; -> Option DQuick Check:
Trait object syntax = &dyn Trait [OK]
- Omitting the dyn keyword
- Trying to use trait name without reference
- Assigning trait object without pointer or reference
trait Speak {
fn speak(&self) -> String;
}
struct Dog;
impl Speak for Dog {
fn speak(&self) -> String {
"Woof".to_string()
}
}
struct Cat;
impl Speak for Cat {
fn speak(&self) -> String {
"Meow".to_string()
}
}
fn animal_speak(animal: &dyn Speak) {
println!("{}", animal.speak());
}
fn main() {
let dog = Dog;
let cat = Cat;
animal_speak(&dog);
animal_speak(&cat);
}Solution
Step 1: Understand trait implementations
Dog and Cat both implement the Speak trait with their own speak() returning "Woof" and "Meow" respectively.Step 2: Analyze function calls with trait objects
animal_speak takes a reference to a dyn Speak trait object and calls speak(), which uses dynamic dispatch to call the correct method.Final Answer:
Woof\nMeow -> Option AQuick Check:
Dynamic dispatch calls correct speak() = Woof then Meow [OK]
- Expecting static dispatch output
- Confusing struct names with method output
- Thinking code causes compilation error
trait Draw {
fn draw(&self);
}
struct Circle;
impl Draw for Circle {
fn draw(&self) {
println!("Drawing Circle");
}
}
fn main() {
let c = Circle;
let obj: Box<Draw> = Box::new(c);
obj.draw();
}Solution
Step 1: Check trait object syntax in Box
Trait objects require thedynkeyword when used as a pointer type likeBox.Step 2: Identify missing dyn keyword
The code usesBox<Draw>instead ofBox, causing a syntax error.Final Answer:
Missing dyn keyword in Box type declaration -> Option BQuick Check:
Box trait object needs dyn keyword [OK]
- Omitting dyn keyword in trait object types
- Assuming trait implementation is missing
- Thinking Box cannot call trait methods
area() method at runtime. Which approach correctly uses trait objects to achieve this?trait Shape {
fn area(&self) -> f64;
}
struct Circle { radius: f64 }
struct Square { side: f64 }
impl Shape for Circle {
fn area(&self) -> f64 { 3.14 * self.radius * self.radius }
}
impl Shape for Square {
fn area(&self) -> f64 { self.side * self.side }
}
fn main() {
// Fill in the blank
let shapes: Vec> = vec![
Box::new(Circle { radius: 2.0 }),
Box::new(Square { side: 3.0 }),
];
for shape in shapes.iter() {
println!("Area: {}", shape.area());
}
}Solution
Step 1: Understand trait object storage
Trait objects must be stored behind pointers like Box or references because their size is unknown at compile time.Step 2: Analyze vector type and initialization
UsingVec>withBox::new()for each shape correctly stores different types implementing Shape.Final Answer:
Use Vec> with Box::new for each shape -> Option CQuick Check:
Trait objects need pointers like Box in collections [OK]
- Trying to store trait objects directly without pointers
- Using Vec<Shape> which is invalid
- Using references without ensuring lifetime
Ready to prove it?
Pass the test and this pattern moves unknown
