CodeBlanks Logo Icon CodeBlanks

Summary - Trait objects

Combine Box<dyn Trait> and the vtable to build a list of shapes.

Trait objects summary

A trait object is a fat pointer made of a data pointer and a vtable; it's used as &dyn T, &mut dyn T, Box<dyn T> and Rc<dyn T>. It's needed for mixed collections and runtime choices; the price is an indirect call. The trait must be dyn compatible; incompatible methods are excluded with where Self: Sized. To get back to the concrete type, std::any::Any and downcast_ref are used.

Back to the concrete type with Any

dyn Any is a special trait object that can hold any 'static type. Which type it is can be queried at runtime:

use std::any::Any;

fn tanimla(o: &dyn Any) -> String {
    if let Some(n) = o.downcast_ref::<i32>() {
        format!("tamsayı {n}")
    } else if let Some(s) = o.downcast_ref::<&str>() {
        format!("metin: {s}")
    } else {
        String::from("bilinmeyen")
    }
}

Any is a last resort in design; an enum or a trait method is usually a better solution.

Practice Type query

Write the tanimla function above. Keep 5, "metin" and 2.5 in a Vec<Box<dyn Any>>; print the result for each on its own line.

Starter code

main.rs
use std::any::Any;

fn main() {
    // buraya yaz
}

Sample solution

main.rs
use std::any::Any;

fn tanimla(o: &dyn Any) -> String {
    if let Some(n) = o.downcast_ref::<i32>() {
        format!("tamsayı {n}")
    } else if let Some(s) = o.downcast_ref::<&str>() {
        format!("metin: {s}")
    } else {
        String::from("bilinmeyen")
    }
}

fn main() {
    let ogeler: Vec<Box<dyn Any>> = vec![Box::new(5), Box::new("metin"), Box::new(2.5)];
    for o in &ogeler {
        println!("{}", tanimla(o.as_ref()));
    }
}

Expected output

tamsayı 5
metin: metin
bilinmeyen

Questions and answers

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

1 What does <code>downcast_ref::&lt;T&gt;()</code> return?

  • It panics
  • Box<T>
  • Some(&T) if the value really is T, None otherwise Correct answer
  • Always T

Explanation: Any carries the type's identity (TypeId); downcast compares that identity.

2 Test the type

Question
if let Some(n) = o.____::<i32>() {
    println!("tamsayı {n}");
}
Answer
if let Some(n) = o.downcast_ref::<i32>() {
    println!("tamsayı {n}");
}

Explanation: downcast_ref gives a reference if the value is of that type.

3 Group summary

Term Meaning
vtable Method table
Box<dyn T> Owned trait object
where Self: Sized Excluded from dyn
dyn Any Runtime type query

All lessons in Advanced