Trait Objects & dyn
A trait object — written behind a pointer like or — lets values of different concrete types be used through one shared interface, with the right method chosen at runtime.
Learn Trait Objects & dyn in our free Rust course — a beginner-friendly interactive lesson with worked examples, a practice exercise and a quick reference.
Part of the free Rust course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
In this lesson you'll learn how trait objects differ from generics, how to store mixed types in a , the difference between static and dynamic dispatch, and the object-safety rules that decide when a trait can become a trait object.
What You'll Learn in This Lesson
1️⃣ The dyn Keyword: One Interface, Many Types
When you write , you're saying "a reference to some type that implements Animal — I don't care which." This is a trait object . Unlike a concrete type, its exact identity is only known at runtime, so the call to a.name() is dispatched dynamically. The dyn keyword makes that runtime nature explicit in the type.
A single describe function handles every animal. There is exactly one copy of describe in the binary, and at each call the program follows the trait object's vtable to find the correct name and sound implementation.
2️⃣ Box<dyn Trait>: Mixing Types in a Vec
A Vec requires all its elements to be the same type, so you can't put a Circle and a Square directly in one Vec . The fix is to box each value as a trait object: . Each owns one heap value and has a uniform pointer size, so they all fit in one vector.
Use when you only need to borrow for a call, and when you need to own the value — in a collection, as a struct field, or as a return type that might be one of several types.
3️⃣ Static vs Dynamic Dispatch
Generics give you static dispatch : the compiler stamps out a specialized copy of the function for each concrete type (monomorphization), so calls are resolved at compile time and can be inlined. Trait objects give you dynamic dispatch : one shared function, with each call routed through a vtable at runtime. Compare the two side by side below.
Both print the same thing, but the machine code differs: greet_static is compiled twice (one version per type), while greet_dynamic exists once and looks up the method at runtime. Prefer generics for speed and single-type call sites; prefer trait objects when you need to store or pass mixed types.
Your turn. Fill in the blanks marked ___ , then run it.
Build a small notification system where many channels share one Notifier trait and live together in a . Run it with cargo run and check the output.
📋 Quick Reference — Trait Objects
Practice quiz
What does dyn Trait represent in Rust?
- A compile-time generic
- A macro
- A trait object resolved at runtime
- A lifetime
Answer: A trait object resolved at runtime. dyn Trait is a trait object: the concrete type is known only at runtime via a vtable.
Why can't you write a bare dyn Trait by value, like fn f(x: dyn Trait)?
- Its size is not known at compile time
- dyn is reserved
- Traits cannot be parameters
- It needs a lifetime
Answer: Its size is not known at compile time. A bare trait object is unsized, so it must sit behind a pointer like &dyn Trait or Box<dyn Trait>.
Which type lets you OWN a trait object on the heap?
- &dyn Trait
- impl Trait
- dyn Trait
- Box<dyn Trait>
Answer: Box<dyn Trait>. Box<dyn Trait> owns one heap-allocated value; the box itself has a known pointer size.
How can you store mixed concrete types that share one trait in a Vec?
- Vec<dyn Trait>
- Vec<Box<dyn Trait>>
- Vec<impl Trait>
- Vec<Trait>
Answer: Vec<Box<dyn Trait>>. Each element must have the same size, so box each as Vec<Box<dyn Trait>>.
Trait objects use which kind of dispatch?
- Dynamic dispatch via a vtable
- Static dispatch via monomorphization
- No dispatch
- Inline dispatch only
Answer: Dynamic dispatch via a vtable. Each method call is looked up at runtime through the trait object's vtable.
Generics like fn f<T: Trait>(x: T) use which kind of dispatch?
- Dynamic dispatch
- Runtime vtable lookup
- Static dispatch
- Reflection
Answer: Static dispatch. The compiler stamps out a specialized copy per concrete type, resolved at compile time.
A trait object reference is described as a fat pointer because it holds...
- Two data pointers
- A data pointer plus a vtable pointer
- A length and a capacity
- A pointer and a lifetime
Answer: A data pointer plus a vtable pointer. It is two pointers wide: one to the data and one to the vtable of method pointers.
Which compiler error means a trait is not object safe?
- E0277: trait not implemented
- E0502: borrow conflict
- E0412: cannot find type
- E0038: cannot be made into an object
Answer: E0038: cannot be made into an object. E0038 fires when a trait breaks object-safety rules, such as returning Self.
Which makes a trait NOT object safe?
- A method taking &self
- A method that returns Self
- A method returning String
- A default method
Answer: A method that returns Self. Returning Self needs a known size, which a trait object lacks, so it breaks object safety.
When should you prefer a generic over dyn Trait?
- When you need mixed types in a Vec
- When the type is unknown until runtime
- When all values are one known type
- When returning one of several types
Answer: When all values are one known type. If the type is fixed, generics give static dispatch with no runtime cost.