Trait Objects

We’ve seen how a function can take arguments which implement a trait:

use std::fmt::Display;

fn print<T: Display>(x: T) {
    println!("Your value: {}", x);
}

fn main() {
    print(123);
    print("Hello");
}

However, how can we store a collection of mixed types which implement Display?

fn main() {
    let xs = vec![123, "Hello"];
}

For this, we need trait objects:

use std::fmt::Display;

fn main() {
    let xs: Vec<Box<dyn Display>> = vec![Box::new(123), Box::new("Hello")];
    for x in xs {
        println!("x: {x}");
    }
}

Memory layout after allocating xs:

<str as Display>::fmt<i32 as Display>::fmtStackHeapxsptrlen2capacity2Hello7b000000

Similarly, you need a trait object if you want to return different values implementing a trait:

fn numbers(n: i32) -> Box<dyn Iterator<Item=i32>> {
    if n > 0 {
        Box::new(0..n)
    } else {
        Box::new((n..0).rev())
    }
}

fn main() {
    println!("{:?}", numbers(-5).collect::<Vec<_>>());
    println!("{:?}", numbers(5).collect::<Vec<_>>());
}