Rust
dyn Trait
Learn the dyn Trait trait object for dynamic dispatch.
dyn Trait
When different types need to live in the same collection, generics aren't enough, because Vec<T> wants a single T. A trait object (dyn Trait) solves this; method calls go through a table (vtable) at runtime:
let sekiller: Vec<Box<dyn Sekil>> = vec![
Box::new(Kare(2.0)),
Box::new(Daire(1.0)),
];
for s in &sekiller {
println!("{}: {:.2}", s.ad(), s.alan());
}
The size of dyn Trait isn't known at compile time, so it's always used behind a pointer: Box<dyn T>, &dyn T, Rc<dyn T>.
Object safety
Not every trait can be used with dyn. To be a trait object, its methods must not return Self or take generic type parameters. For example, Clone (fn clone(&self) -> Self) isn't object safe:
// let v: Vec<Box<dyn Clone>>; // error E0038
let v: Vec<Box<dyn std::fmt::Display>> = vec![Box::new(5), Box::new("beş")];
for x in &v { print!("{x} "); } // 5 beş
Exercise Shape list
Implement the Sekil trait with the methods ad and alan for Kare(f64) and Daire(f64). Keep Kare(2.0) and Daire(1.0) in a Vec<Box<dyn Sekil>>; print each as ad: alan ({:.2}), then toplam: X.
Show example solution
trait Sekil {
fn alan(&self) -> f64;
fn ad(&self) -> String;
}
struct Kare(f64);
struct Daire(f64);
impl Sekil for Kare {
fn alan(&self) -> f64 {
self.0 * self.0
}
fn ad(&self) -> String {
String::from("kare")
}
}
impl Sekil for Daire {
fn alan(&self) -> f64 {
std::f64::consts::PI * self.0 * self.0
}
fn ad(&self) -> String {
String::from("daire")
}
}
fn main() {
let sekiller: Vec<Box<dyn Sekil>> = vec![Box::new(Kare(2.0)), Box::new(Daire(1.0))];
let mut toplam = 0.0;
for s in &sekiller {
println!("{}: {:.2}", s.ad(), s.alan());
toplam += s.alan();
}
println!("toplam: {:.2}", toplam);
}
Test yourself
-
What is the main trade-off between static dispatch (generics) and dynamic dispatch (dyn)?
- No difference
- Generics are faster but one type; dyn is flexible but has a small runtime cost
- dyn is always faster
- Generics are resolved at runtime
Show answer
Generics are faster but one type; dyn is flexible but has a small runtime cost — Generic code can be inlined; a dyn call is indirect through a pointer and can't be inlined.
-
Trait object
let sekiller: Vec<Box<____ Sekil>> = vec![Box::new(Kare(2.0))];
Show answer
let sekiller: Vec<Box<dyn Sekil>> = vec![Box::new(Kare(2.0))];
-
Type and dispatch
- fn f<T: Sekil>(x: &T)
- Static
- fn f(x: &dyn Sekil)
- Dynamic
- Vec<Box<dyn Sekil>>
- Mixed types
- Box<dyn Clone>
- E0038
Next lesson: Summary - Trait bounds