1
0
mirror of https://github.com/google/comprehensive-rust.git synced 2025-06-23 01:07:40 +02:00
Files
comprehensive-rust/src/generics/trait-objects.md
2022-12-21 16:38:28 +01:00

3.1 KiB

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:

 Stack                             Heap
.- - - - - - - - - - - - - -.     .- - - - - - - - - - - - - - - - - - - - - - - -.
:                           :     :                                               :
:    xs                     :     :                                               :
:   +-----------+-------+   :     :   +-----+-----+                               :
:   | ptr       |   o---+---+-----+-->| o o | o o |                               :
:   | len       |     2 |   :     :   +-|-|-+-|-|-+                               :
:   | capacity  |     2 |   :     :     | |   | |   +----+----+----+----+----+    :
:   +-----------+-------+   :     :     | |   | '-->| H  | e  | l  | l  | o  |    :
:                           :     :     | |   |     +----+----+----+----+----+    :
`- - - - - - - - - - - - - -'     :     | |   |                                   :
                                  :     | |   |     +-------------------------+   :
                                  :     | |   '---->| "<str as Display>::fmt" |   :
                                  :     | |         +-------------------------+   :
                                  :     | |                                       :
                                  :     | |   +-------------------------+         :
                                  :     | '-->| "<i32 as Display>::fmt" |         :
                                  :     |     +-------------------------+         :
                                  :     |                                         :
                                  :     |     +----+----+----+----+               :
                                  :     '---->| 7b | 00 | 00 | 00 |               :
                                  :           +----+----+----+----+               :
                                  :                                               :
                                  :                                               :
                                  '- - - - - - - - - - - - - - - - - - - - - - - -'

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<_>>());
}