2022-12-21 16:36:30 +01:00
# Trait Objects
We've seen how a function can take arguments which implement a trait:
```rust,editable
use std::fmt::Display;
fn print< T: Display > (x: T) {
2023-02-09 07:48:18 +01:00
println!("Your value: {x}");
2022-12-21 16:36:30 +01:00
}
fn main() {
print(123);
print("Hello");
}
```
However, how can we store a collection of mixed types which implement `Display` ?
```rust,editable,compile_fail
fn main() {
2023-03-28 15:42:56 -04:00
let displayables = vec![123, "Hello"];
2022-12-21 16:36:30 +01:00
}
```
For this, we need _trait objects_ :
```rust,editable
use std::fmt::Display;
fn main() {
2023-03-28 15:42:56 -04:00
let displayables: Vec< Box < dyn Display > > = vec![Box::new(123), Box::new("Hello")];
for x in displayables {
2022-12-21 16:36:30 +01:00
println!("x: {x}");
}
}
```
2023-03-28 15:42:56 -04:00
Memory layout after allocating `displayables` :
2022-12-21 16:36:30 +01:00
```bob
Stack Heap
.- - - - - - - - - - - - - -. .- - - - - - - - - - - - - - - - - - - - - - - -.
: : : :
2023-03-28 15:42:56 -04:00
: displayables : : :
2022-12-21 16:36:30 +01:00
: +-----------+-------+ : : +-----+-----+ :
: | ptr | o---+---+-----+-->| o o | o o | :
: | len | 2 | : : +-|-|-+-|-|-+ :
: | capacity | 2 | : : | | | | +----+----+----+----+----+ :
: +-----------+-------+ : : | | | '-->| H | e | l | l | o | :
: : : | | | +----+----+----+----+----+ :
`- - - - - - - - - - - - - -' : | | | :
: | | | +-------------------------+ :
: | | '---->| "< str as Display > ::fmt" | :
: | | +-------------------------+ :
: | | :
2023-01-16 15:41:17 +00:00
: | | +----+----+----+----+ :
: | '-->| 7b | 00 | 00 | 00 | :
2022-12-21 16:36:30 +01:00
: | +----+----+----+----+ :
2023-01-16 15:41:17 +00:00
: | :
: | +-------------------------+ :
: '---->| "< i32 as Display > ::fmt" | :
: +-------------------------+ :
2022-12-21 16:36:30 +01:00
: :
: :
'- - - - - - - - - - - - - - - - - - - - - - - -'
```
2023-02-09 21:51:08 +00:00
Similarly, you need a trait object if you want to return different types
2022-12-21 16:36:30 +01:00
implementing a trait:
```rust,editable
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 < _ > >());
}
```
2023-03-28 15:42:56 -04:00
< details >
* Types that implement a given trait may be of different sizes. This makes it impossible to have things like `Vec<Display>` in the example above.
* `dyn Display` is a way to tell the compiler about a dynamically sized type that implements `Display` .
* In the example, `displayables` holds *fat pointers* to objects that implement `Display` . The fat pointer consists of two components, a pointer to the actual object and a pointer to the virtual method table for the `Display` implementation of that particular object.
* Compare these outputs in the above example:
```rust,ignore
use std::fmt::Display;
println!("{}", std::mem::size_of::< u32 > ());
println!("{}", std::mem::size_of::< &u32>());
println!("{}", std::mem::size_of::< & dyn Display>());
println!("{}", std::mem::size_of::< Box < dyn Display > >());
```
< / details >