mirror of
https://github.com/google/comprehensive-rust.git
synced 2025-03-31 09:42:11 +02:00
* don't explain default trait methods early * talk about Iterator before IntoIterator * Defer discussion of trait objects to that chapter * be more specific about turbofish, in speaker notes
4.0 KiB
4.0 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 displayables = vec![123, "Hello"];
}
For this, we need trait objects:
use std::fmt::Display;
fn main() {
let displayables: Vec<Box<dyn Display>> = vec![Box::new(123), Box::new("Hello")];
for x in displayables {
println!("x: {x}");
}
}
Memory layout after allocating displayables
:
Stack Heap
.- - - - - - - - - - - - - -. .- - - - - - - - - - - - - - - - - - - - - - - -.
: : : :
: displayables : : :
: +-----------+-------+ : : +-----+-----+ :
: | ptr | o---+---+-----+-->| o o | o o | :
: | len | 2 | : : +-|-|-+-|-|-+ :
: | capacity | 2 | : : | | | | +----+----+----+----+----+ :
: +-----------+-------+ : : | | | '-->| H | e | l | l | o | :
: : : | | | +----+----+----+----+----+ :
`- - - - - - - - - - - - - -' : | | | :
: | | | +-------------------------+ :
: | | '---->| "<str as Display>::fmt" | :
: | | +-------------------------+ :
: | | :
: | | +----+----+----+----+ :
: | '-->| 7b | 00 | 00 | 00 | :
: | +----+----+----+----+ :
: | :
: | +-------------------------+ :
: '---->| "<i32 as Display>::fmt" | :
: +-------------------------+ :
: :
: :
'- - - - - - - - - - - - - - - - - - - - - - - -'
Similarly, you need a trait object if you want to return different types 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<_>>());
}
- 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 implementsDisplay
.- In the example,
displayables
holds fat pointers to objects that implementDisplay
. The fat pointer consists of two components, a pointer to the actual object and a pointer to the virtual method table for theDisplay
implementation of that particular object. - Compare these outputs in the above example:
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>>());