0
0
Rustprogramming~10 mins

Trait objects overview in Rust - Step-by-Step Execution

Choose your learning style9 modes available
Concept Flow - Trait objects overview
Define Trait
Implement Trait for Types
Create Trait Object (Box<dyn Trait>)
Use Trait Object via Trait Methods
Dynamic Dispatch at Runtime
Program Uses Different Types Uniformly
This flow shows how Rust uses trait objects to allow different types to be handled through a common interface with dynamic dispatch.
Execution Sample
Rust
trait Draw {
    fn draw(&self);
}

struct Circle;
impl Draw for Circle {
    fn draw(&self) { println!("Drawing Circle"); }
}

fn main() {
    let shape: Box<dyn Draw> = Box::new(Circle);
    shape.draw();
}
This code defines a trait, implements it for a type, creates a trait object, and calls a method via dynamic dispatch.
Execution Table
StepActionCode LineState/ValueOutput
1Define trait Draw with method drawtrait Draw { fn draw(&self); }Trait Draw defined
2Define struct Circlestruct Circle;Type Circle defined
3Implement Draw for Circleimpl Draw for Circle { fn draw(&self) { ... } }Circle implements Draw
4Create trait object shape as Box<dyn Draw>let shape: Box<dyn Draw> = Box::new(Circle);shape holds Circle as trait object
5Call draw() on shape (dynamic dispatch)shape.draw();Calls Circle's draw method via trait objectDrawing Circle
6End of execution
💡 Program ends after calling draw() on the trait object.
Variable Tracker
VariableStartAfter Step 4After Step 5Final
shapeuninitializedBox containing Circle as dyn DrawSame (used to call draw)Same
Key Moments - 3 Insights
Why do we use Box<dyn Draw> instead of just Circle?
Box<dyn Draw> allows storing different types that implement Draw behind a pointer, enabling dynamic dispatch as shown in step 4 and 5 of the execution_table.
How does Rust know which draw() method to call at runtime?
Rust uses dynamic dispatch through the trait object (step 5), which stores a pointer to the actual method implementation for the concrete type.
Can we call draw() directly on Circle without a trait object?
Yes, but then we lose the ability to treat different types uniformly. Trait objects enable calling draw() on any type implementing Draw via the same interface.
Visual Quiz - 3 Questions
Test your understanding
Look at the execution_table, what does the variable 'shape' hold after step 4?
AA Circle value directly
BA Box containing Circle as a trait object
CAn uninitialized variable
DA reference to the Draw trait
💡 Hint
Check the 'State/Value' column in row for step 4 in execution_table.
At which step does dynamic dispatch happen according to the execution_table?
AStep 3
BStep 4
CStep 5
DStep 2
💡 Hint
Look for the step where draw() is called on the trait object.
If we replaced Box<dyn Draw> with just Circle, what would change in the variable_tracker?
Ashape would hold a Circle value directly, no trait object
Bshape would still hold a trait object
Cshape would be uninitialized
Dshape would hold a reference to Draw trait
💡 Hint
Refer to variable_tracker and think about what Box means.
Concept Snapshot
Trait objects let Rust handle different types through a common interface.
Use Box<dyn Trait> to create a trait object.
Calls on trait objects use dynamic dispatch at runtime.
This enables polymorphism without generics.
Trait objects store a pointer to data and method table.
Useful for heterogeneous collections or uniform APIs.
Full Transcript
This visual trace shows how Rust uses trait objects to enable dynamic dispatch. First, a trait Draw is defined with a method draw. Then, a struct Circle is defined and implements Draw. Next, a trait object shape is created as Box<dyn Draw> holding a Circle. When shape.draw() is called, Rust dynamically dispatches to Circle's draw method. The variable tracker shows shape holds the trait object after creation and remains the same after calling draw. Key moments clarify why Box<dyn Draw> is used and how dynamic dispatch works. The quiz tests understanding of trait objects, dynamic dispatch step, and variable state changes. The snapshot summarizes trait objects as a way to use polymorphism in Rust by storing different types behind a common interface pointer.