The difference between static dispatch (impl Trait) and dynamic dispatch (dyn Trait) — not just syntax, but a real tradeoff.
impl Trait and dyn Trait are not interchangeable. They compile to fundamentally different code.
fn process(handler: impl Handler) {
handler.handle();
}The compiler monomorphizes this: it generates a concrete copy of process for every type that implements Handler. Fast at runtime (no vtable lookup), larger binary, requires the type to be known at compile time.
fn process(handler: &dyn Handler) {
handler.handle();
}This uses a vtable — a pointer to a table of function pointers resolved at runtime. Slower (one pointer dereference per method call), smaller binary, works with heterogeneous collections (Vec<Box<dyn Handler>>).
Use impl Trait when:
Use dyn Trait when:
In DocSee, the CLI variant used impl Trait for its command handlers (always statically known), and the plugin loader used dyn Trait (loaded at runtime from config). Two interfaces, two right answers.
Karanveer Singh Shaktawat
Full Stack Engineer & Infrastructure Architect
Building portfolio, contributing to open source, and seeking remote full-time roles with significant technical ownership.
Pick what you want to hear about — I'll only email when it's worth it.
Did this resonate?
The three rules Rust uses to infer lifetimes automatically, so you understand when you need to annotate and when the compiler handles it for you.
The three ways the frontend talks to the Rust backend in Tauri, when to use each, and the one gotcha that wasted an afternoon.