tezvyn:

Compare enum vs trait objects for heterogeneous shapes in Rust

AI-drafted, machine-checkedSource: doc.rust-lang.orgadvanced

This tests compile-time vs run-time polymorphism in Rust. A strong answer contrasts enum's closed set, static dispatch, and stack layout against trait objects' open extensibility, heap allocation, and vtable indirection.

WHAT THIS TESTS: This question probes your grasp of static versus dynamic polymorphism in Rust and whether you can reason about memory layout, dispatch mechanisms, and API evolution. Interviewers want to see that you understand the trade-off between a closed set of types known at compile time and an open set extensible by downstream code.

A GOOD ANSWER COVERS: First, the enum approach wraps Circle, Square, and other variants into a single sized type like Shape, which lets you store a Vec<Shape> with no pointers, no heap allocation, and no vtable overhead. Method calls use static dispatch through match arms, which allows the compiler to inline and optimize each branch. The downside is that the set is closed; adding a Triangle requires editing the enum definition and every exhaustive match across the codebase. Second, the trait object approach uses Vec<Box<dyn Draw>>, which stores fat pointers consisting of a data address and a vtable pointer. This creates an open set where new types can be added in other crates without recompiling the collection code, but every draw call incurs dynamic dispatch, the objects live on the heap, and iteration suffers from pointer chasing and cache misses. Third, performance specifics: enums keep data contiguous and benefit from cache locality, while trait objects scatter allocations and add a vtable dereference per call. Fourth, flexibility: enums suit internal domain models where you own all variants, whereas trait objects suit plugin systems or when you need heterogeneous collections across crate boundaries.

COMMON WRONG ANSWERS: Claiming that dyn Trait inherently requires Box or heap allocation, when the real constraint is that dyn Trait is unsized and therefore needs indirection such as Box, Rc, or references. Asserting that trait objects are simply slower without mentioning cache locality, inlining barriers, or the cost of heap allocation. Suggesting that enums cannot be iterated polymorphically or that they waste massive amounts of memory; while enums do size to the largest variant plus a discriminant, they are often more compact than scattered heap allocations. Ignoring object safety rules that prevent certain traits from becoming trait objects, such as generic methods or Self return types.

LIKELY FOLLOW-UPS: How could you avoid heap allocation while keeping an open set? You might discuss generics with Vec<T: Draw> at the cost of homogeneity, or crates like enum_dispatch that macro-generate enums from traits. What happens to enum size if one variant is vastly larger than the others? The enum inflates to the largest variant, potentially wasting space for smaller ones. When is a trait object preferable to an enum even inside a single crate? When the variant count is large and match arms become unwieldy, or when you need runtime configuration driven by external data. How does Pin<Box<dyn Future>> relate to these concepts? It demonstrates that trait objects are ubiquitous in async Rust for erasing concrete future types.

ONE CONCRETE EXAMPLE: Consider a particle system with ten thousand entities. Using an enum Particle { Fire { radius: f32 }, Smoke { density: f32 }, Spark { life: u8 } } inside a Vec<Particle> stores everything in one contiguous array with a one-byte discriminant and inline fields. Updating all particles in a loop traverses a single cache-friendly memory region and dispatches statically via match. Switching to Vec<Box<dyn Particle>> forces ten thousand separate heap allocations, stores sixteen-byte fat pointers in the vector, and adds a vtable lookup for every update and render call. Benchmarks often show the enum version iterating two to five times faster, but adding a new particle type means editing the enum and every match that handles it.

Read the original → doc.rust-lang.org

Get five bites like this every day.

Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.