Rust
Static vs dynamic dispatch
Learn the difference and cost of static vs dynamic dispatch.
Static and dynamic dispatch
The same behaviour can be written in two ways:
fn statik<T: Ses>(x: &T) -> String { // a separate copy for each T
x.ses() // direct, can be inlined
}
fn dinamik(x: &dyn Ses) -> String { // a single copy
x.ses() // through the vtable
}
Static: faster calls, the compiler can inline and optimize; the price is a bigger binary and compile time. Dynamic: one copy of code, mixed collections, smaller binary; the price is an indirect call and missed optimizations.
How to choose?
The default choice is generics. Switch to dyn when: you need different types in a collection, the type is decided at runtime (plugins, configuration), compile time or binary size has become a problem, or you don't want the API polluted with generic parameters. The performance difference is too small to measure in most applications; measure in hot loops.
// mixed: generics aren't enough
let v: Vec<Box<dyn Ses>> = vec![Box::new(Kedi), Box::new(Kopek)];
// single type: generics are enough
fn hepsi<T: Ses>(v: &[T]) -> Vec<String> { v.iter().map(|x| x.ses()).collect() }
Exercise Two dispatches
Write the statik and dinamik functions above. Call both with Kedi and print as statik: .. | dinamik: ...
Show example solution
trait Ses {
fn ses(&self) -> String;
}
struct Kedi;
struct Kopek;
impl Ses for Kedi {
fn ses(&self) -> String {
String::from("Miyav")
}
}
impl Ses for Kopek {
fn ses(&self) -> String {
String::from("Hav")
}
}
fn statik<T: Ses>(x: &T) -> String {
x.ses()
}
fn dinamik(x: &dyn Ses) -> String {
x.ses()
}
fn main() {
let k = Kedi;
println!("statik: {} | dinamik: {}", statik(&k), dinamik(&k));
}
Test yourself
-
If a function takes <code>&dyn Ses</code>, can a <code>&Kedi</code> be passed to it?
- With as
- Yes; &Kedi converts automatically to &dyn Ses (unsize coercion)
- No
- Only with a Box
Show answer
Yes; &Kedi converts automatically to &dyn Ses (unsize coercion) — The compiler adds the vtable pointer in the conversion; the reverse (dyn → concrete) needs a downcast with Any.
-
Static dispatch
fn statik____(x: &T) -> String {
x.ses()
}
Show answer
fn statik<T: Ses>(x: &T) -> String {
x.ses()
}
-
Dispatch and property
- Statik (generic)
- Can be inlined
- Dinamik (dyn)
- Call via vtable
- Monomorfizasyon
- A copy per type
- Unsize coercion
- &Kedi → &dyn Ses
Next lesson: Object safety