In this lesson, you will learn why moving from concrete generic types to trait objects often breaks your code by violating Sized requirements.
When you use generics, Rust uses static dispatch. The compiler knows the exact size of your type at compile time, and it generates a specialized version of your function for that type. However, when you coerce a type into a trait object (e.g., Box<dyn Trait>), you move to dynamic dispatch. This erases the specific type information, replacing it with a pointer to a vtable.
The most common pitfall occurs when you try to treat a generic function as if it can handle trait objects without explicit constraints.
The Sized Trap
Consider this generic function:
fn process<T: MyTrait>(item: T) {
item.do_work();
}
This works perfectly with concrete types because T implicitly carries a Sized bound. If you try to pass &dyn MyTrait into this, or try to change the signature to accept dyn MyTrait, the compiler will complain. Why? Because dyn MyTrait is an "unsized" type—its size is unknown at compile time.
If you accidentally write a generic function that requires the size to be known (to be passed by value, for instance), you cannot pass a trait object to it. You have to decide: do I want the performance of static dispatch (generics), or the flexibility of dynamic dispatch (trait objects)?
Implicit Coercion Failures
Another pitfall occurs when you try to coerce a generic type into a trait object inside a function that expects a specific concrete type. If you have a struct defined as struct Container<T: Trait>, you cannot store Box<dyn Trait> inside it, because T expects a single, fixed-size type. You cannot "upgrade" a generic container to hold trait objects later; the abstraction layer is baked into the struct’s memory layout.
When you feel the urge to switch from generics to trait objects, remember that you are trading compile-time size knowledge for runtime pointer indirection. If your code stops compiling after adding dyn, check if you are trying to pass a dynamically sized type into a function that implicitly assumes it knows the size of the input.
Key Takeaway: Trait object coercion fails when your logic relies on the compiler knowing the size of a type, as dyn Trait is unsized and cannot be treated like a concrete generic parameter.