ProgrammingBackend Developer

Explain the method dispatch mechanism for trait objects (dynamic dispatch) and how it differs from static dispatch in Rust.

Pass interviews with Hintsage AI assistant

Answer.

Dispatch is the mechanism for selecting a specific function (method) to call. Rust has two approaches: static and dynamic dispatch.

Background:

In OOP languages, dynamic method calls typically use a vtable (virtual table). Rust implements a similar mechanism for trait objects — references to objects of types that implement certain traits. Static dispatch occurs when using generics and trait bounds.

Problem:

Often, one has to choose between flexibility (the ability to work with objects of different types through a single interface) and performance (static dispatch allows for inlining methods). An incorrect choice leads either to overly complex generics or performance losses.

Solution:

Static dispatch is achieved through generic parameters: the compiler generates separate code for each type. Dynamic dispatch occurs if a function takes an argument of type &dyn Trait or Box<dyn Trait>, then when calling a method by trait Rust looks in the vtable by address, as in classic OOP languages.

Code example:

trait Shape { fn area(&self) -> f64; } impl Shape for Circle { fn area(&self) -> f64 { 3.1415 * self.radius * self.radius } } fn print_area(shape: &dyn Shape) { // dynamic dispatch println!("area = {}", shape.area()); } // Or statically: fn print_area_static<S: Shape>(shape: &S) { println!("area = {}", shape.area()); }

Key features:

  • dyn Trait uses vtable (dynamic dispatch)
  • generics are called at compile time (static dispatch)
  • Work with different tradeoffs on speed and flexibility

Tricky questions.

Can you make Box<dyn Sized>?

No. dyn Trait is by definition unsized, always requires the use of Box, Arc, or references, but not 'Box<dyn Sized>' — this makes no sense. Sized traits are not possessed by trait objects.

Is dyn Trait allowed for traits with generic methods?

No. You cannot create object-safe traits with generic methods (this is often confused!); composite types are not object-safe:

trait MyTrait { fn foo<T>(&self, x: T); } let x: &dyn MyTrait = ... // Compilation error!

Can you make dyn Trait for a trait with Self values in the signature?

No, if the method returns Self (many do not understand this nuance: object safety requires that Self not be in the signature; self can only be in arguments, but not in the return value).

Common mistakes and anti-patterns

  • Abuse of dyn Trait where static dispatch is suitable
  • Attempts to use generic methods or Associated Types with dyn Trait (the compiler will prohibit it)
  • Non-obvious performance leaks in "thin" areas (frequent calls)

Real-life example

Negative case

Everywhere dyn Trait was used for interface universality, even inside tight loops where generics could suffice.

Pros:

  • Flexibility, easy interface extension without recompilation

Cons:

  • Losses of up to 15-30% in performance on method calls, inability to inline

Positive case

Static dispatch was used in the internal logic, while dyn Trait was used only at the boundaries of modules.

Pros:

  • Maximally fast code inside modules
  • Flexibility of the API at the public boundary

Cons:

  • Requires thoughtful API design, more generic functions