-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Ownership and Borrowing
-
Section 4: Structuring Data
-
Section 5: Collections and Error Handling
-
Section 6: Traits and Generics
-
Section 7: Concurrency
-
Section 8: Building for the Web
-
Section 9: Memory and Performance
-
Section 10: More Standard Library and Ecosystem
-
Section 11: Advanced Rust
-
Section 12: Rust for Systems and WebAssembly
-
Section 13: Tooling and Best Practices
-
Section 14: Data Structures and Algorithms in Rust
-
Section 15: Practical Projects
-
Section 16: Interview Practice
-
Section 17: std::collections In Depth
-
Section 18: std::io and std::fs In Depth
-
Section 19: std::net
-
Section 20: std::option and std::result In Depth
-
Section 21: std::iter In Depth
-
Section 22: std::sync In Depth
-
Section 23: std::string and std::str
-
Section 24: Cargo and Crates.io Ecosystem
-
Section 25: Popular Crates Ecosystem
-
Section 26: Rust Memory Model Deep Dive
-
Section 27: More Practice Exercises
-
Section 28: More Interview Practice
-
Section 29: Async Rust Deep Dive
-
Section 30: Tokio Ecosystem In Depth
-
Section 31: Error Handling Ecosystem Deep Dive
-
Section 32: Serde In Depth
-
Section 33: Web Development with Rust Deep Dive
-
Section 34: Database Access Ecosystem
-
Section 35: Rust for Embedded Systems Deep Dive
-
Section 36: Rust Macros In Depth
-
Section 37: Advanced Trait System
-
Section 38: Unsafe Rust In Depth
-
Section 39: Rust CLI Development
-
Section 40: Testing Ecosystem Deep Dive
-
Section 41: WebAssembly Deep Dive
-
Section 42: Rust Design Patterns
-
Section 43: More Data Structures in Rust
-
Section 44: Final Practice Projects
-
Section 45: Rust Performance Optimization
-
Section 46: Rust Ecosystem Tooling
-
Section 47: More Interview and Review
143: Common Rust Interview Questions on Trait Objects vs Generics
I once interviewed a candidate who was technically brilliant but stumbled hard on a surprisingly simple question. He had built a sophisticated plugin system for a game engine, and when I asked why he chose Box<dyn Plugin> for his registry instead of a generic T: Plugin, he froze. He knew his code worked, and he'd used the syntax correctly, but he couldn't explain the "why" behind the mechanism. In a high-stakes interview, not knowing the difference between static and dynamic dispatch makes it look like you're guessing with the language rather than mastering it.
The Magic of Monomorphization
When you use generics—like fn execute<T: Task>(task: T)—you're asking Rust to perform static dispatch. I like to think of this as the compiler doing the heavy lifting for you before the program even runs. Through a process called monomorphization, Rust looks at every single place you called execute and generates a dedicated version of that function for every concrete type you used.
If you call it with a ComputeTask and a NetworkTask, the compiler literally writes two different functions in the final binary. This is why generics are blazingly fast; the compiler knows exactly which code to run, meaning it can inline the calls and optimize the hell out of them. The trade-off? Your binary size grows (often called "code bloat") because of all those duplicated versions of the same logic. If you're interviewing for a role working on embedded systems or WASM, this binary size trade-off is exactly what they want to hear you mention.
Navigating the VTable with Trait Objects
Now, generics have a massive limitation: you can't have a Vec<T> that holds different types, even if they all implement the same trait. If you need a list of different things that all "do the same action," you have to move to trait objects, using the dyn keyword (e.g., Box<dyn Task>). This is dynamic dispatch.
Instead of creating a copy of the function for every type, Rust uses a "vtable" (virtual method table). A trait object is actually a fat pointer: it contains one pointer to the data (the instance of your struct) and another pointer to the vtable, which is essentially a map of where the trait's methods are located in memory for that specific type. At runtime, the program follows the pointer to the vtable, finds the right function address, and jumps to it. This indirection is the "cost" of dyn. It's slower than a direct call, and it prevents the compiler from inlining, but it gives you the flexibility to store heterogeneous collections.
Answering the "Which One?" Question
If an interviewer asks you to choose between them, don't just say "generics are faster." That's a textbook answer. Give them the engineering trade-off. Tell them that generics provide the best performance and type safety at the cost of compilation time and binary size. Tell them trait objects provide flexibility and smaller binaries at the cost of a slight runtime overhead and the restriction of "object safety"—the fact that not all traits can be made into trait objects (for instance, if a trait method returns Self, it can't be a trait object because the compiler wouldn't know the size of Self at runtime).
In my experience, the best way to impress is to mention that you'll default to generics for internal logic where performance is key, but switch to dyn at the boundaries of your system—like plugin architectures or event listeners—where you simply cannot know the types at compile time.
📋 Practical Task
Implementing a Heterogeneous Message Dispatcher
Your task is to build a message processing system that can handle different types of alerts (e.g., EmailAlert and SmsAlert) within the same collection. You will implement this using both static and dynamic dispatch to see the difference in practice.
- Define a trait called
Alertwith a methodsend(&self, message: &str). - Create two structs,
EmailAlertandSmsAlert, that implement this trait. - Write a function
send_single_alert<T: Alert>(alert: T, msg: &str)that uses static dispatch to send a single alert. - Create a
Dispatcherstruct that holds aVec<Box<dyn Alert>>. - Implement a method for
Dispatchercalledsend_all(&self, msg: &str)that iterates through the collection and triggerssendon every alert. - In your
mainfunction, demonstrate that you can add both anEmailAlertand anSmsAlertto the sameDispatcherand process them in one loop.
There are no comments for now.