ProgrammingRust Developer

How are traits implemented in Rust and what is dynamic and static dispatch? When should each approach be used?

Pass interviews with Hintsage AI assistant

Answer

Traits in Rust are a way to define an interface: they describe a set of methods that a type must implement. Implementing a trait allows the use of generics and dynamic dispatch of methods.

  • Static dispatch (through generics and bounds): A specific implementation of a method is called at compile time. It uses generic code (impl<T: Trait> or fn foo<T: Trait>(t: T)), and a separate version of the function is created for each type during compilation (monomorphization).

  • Dynamic dispatch: Used when a function takes objects as references to a trait (&dyn Trait), the compiler does not know in advance which type will be used. The method call occurs via vtable (virtual table) at runtime.

Example:

trait Animal { fn speak(&self); } struct Dog; impl Animal for Dog { fn speak(&self) { println!("Woof!"); } } // Static dispatch def print_animal_static<T: Animal>(a: T) { a.speak(); } // Dynamic dispatch def print_animal_dyn(a: &dyn Animal) { a.speak(); }

When to use:

  • Static — when performance is critical and types are known at compile time.
  • Dynamic — when working with a heterogeneous collection of objects or when the type is unknown in advance.

Trick question

Can values with a generic trait type be stored in Vec<Trait>? Why?

Answer: No, this is not possible. Vec requires sized types, and traits themselves have no size. You need to use Box<dyn Trait> or references to traits.

// let v: Vec<Trait> = vec![]; // compilation error let v: Vec<Box<dyn Animal>> = vec![Box::new(Dog)]; // correct

Examples of real errors due to ignorance of the subtleties of the topic


Story

Attempt to create a collection of trait objects without using Box or Rc:

// let coll: Vec<MyTrait> = vec![]; // error: the size for values of type ... cannot be known

As a result — a complete code refactoring and an important lesson on the necessity of using Box for dynamic traits.

Story

A junior developer implemented a complex API using dynamic dispatch where static dispatch could have been, leading to a noticeable performance loss and inlining issues, although all polymorphism was redundant.

Story

On a demo client, attempts were made to store references to temporary objects (&dyn Trait) for interaction with different protocols, leading to dangling references and runtime panic. It was necessary to switch to Box<dyn Trait> with explicit ownership.