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:
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
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
Story
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.