You've used generics for static dispatch: the compiler stamps out a specialized copy per type. Sometimes you need the opposite — one collection holding many different types behind a shared trait. That's dynamic dispatch, via dyn.
Static dispatch (generics)
A generic fn render<T: Draw>(item: &T) is resolved at compile time: each concrete T gets its own compiled version, and the method call is direct — as fast as any function call. The cost is code size (one copy per type) and the constraint that any single call site works with exactly one type.
Dynamic dispatch (dyn Trait)
A &dyn Draw or Box<dyn Draw> is a trait object: a pointer to some value plus a pointer to a table of its trait methods (a vtable). The concrete type is erased; the method is looked up at runtime. This is what lets a Vec<Box<dyn Draw>> hold circles, squares, and text all at once — impossible with a generic, which fixes one type per use.
dyn for many-types-in-one-collection flexibility. Reach for generics by default — they're zero-cost. Reach for dyn Trait when you genuinely need a heterogeneous collection or to store differently-typed values behind one interface, and accept the small vtable indirection.Why Box is usually involved
A trait object has no known size — a circle and a long string differ in bytes — so you can't store one directly on the stack. You put it behind a pointer: Box<dyn Trait> (owned, on the heap) or &dyn Trait (borrowed). The Box gives the trait object a fixed-size handle, which is exactly what the Smart Pointers track is about.