Select Git revision
trait_objects.rs 2.12 KiB
// a struct with generics
// a trait for double
use std::fmt::Debug;
#[derive(Debug)]
struct P<T> {
x: T,
y: T,
}
// Debug is a super trait of Double
// I.e, all types implementing Double must also implement
// Debug, and as a consequence:
// All types implementing Double is ensured to implement
// Debug.
trait Double: Debug {
fn double(&mut self);
}
impl Double for P<u32> {
fn double(&mut self) {
self.x += self.x;
self.y += self.y;
}
}
impl Double for String {
fn double(&mut self) {
let s = self.clone();
self.push_str(&s);
}
}
fn main() {
let mut p1 = P { x: 1u32, y: 2 };
println!("{:?}", p1);
let mut s = "String, ".to_string();
println!("{:?}", s);
let mut v: Vec<&mut dyn Double> = Vec::new();
v.push(&mut p1);
v.push(&mut s);
for e in &mut v {
e.double();
// Here `e` implements Double
// However we don't know at compile time
// which implementation (u32 or String)
// so the compiler will generate
// a dynamic dispatch.
//
// Each `e` will be tagged with a
// vtable index,
// and the corresponding `double` function
// will be called at run time, something like:
//
// `Double.vtable[e.vtable_index](e.data)`;
}
println!("v {:?}", v);
// Here the Debug::format will be called
// on each element of v.
//
// Since debug is a Super trait of Double
// The compiler knows that Debug is implemented
// for each type that implement Double
// (P<u32> and String)
//
// However it does not know at compile time
// the concrete type of the vector elements,
// so it uses dynamic dispatch as above.
}
// Notice, the overhead of dynamic dispatch is
// merely a pointer indirection, so not a big deal right?
//
// Hmmm, not really....
// The performance kill is likely NOT the additional pointer read
// but rather that other optimizations are not possible.
//
// Relate to the specialization of generics, where
// function can be inlined and optimized inside of the caller.
// This is no longer possible.