CodeBlanks Logo Icon CodeBlanks

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ş

Practice 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.

Starter code

main.rs
fn main() {
    // buraya yaz
}

Sample solution

main.rs
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);
}

Expected output

kare: 4.00
daire: 3.14
toplam: 7.14

Questions and answers

Review the lesson questions together with their answers. To test yourself, solve the lesson interactively.

1 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 Correct answer
  • dyn is always faster
  • Generics are resolved at runtime

Explanation: Generic code can be inlined; a dyn call is indirect through a pointer and can't be inlined.

2 Trait object

Question
let sekiller: Vec<Box<____ Sekil>> = vec![Box::new(Kare(2.0))];
Answer
let sekiller: Vec<Box<dyn Sekil>> = vec![Box::new(Kare(2.0))];

Explanation: dyn Sekil represents any type implementing Sekil at runtime.

3 Type and dispatch

Term Meaning
fn f<T: Sekil>(x: &T) Static
fn f(x: &dyn Sekil) Dynamic
Vec<Box<dyn Sekil>> Mixed types
Box<dyn Clone> E0038

All lessons in Intermediate