Trait Objects
A trait object is an instance of dyn Trait accessed behind a pointer or reference (such as &dyn Trait, &mut dyn Trait, Box<dyn Trait>, Rc<dyn Trait>, or Arc<dyn Trait>) that pairs a pointer to a concrete value with a pointer to its implementation of Trait.
Unsized Nature of dyn Trait (!Sized)
Because different types implementing a trait Render can have completely different sizes in memory (for example, a 0-byte unit struct vs. a 64-byte struct), the type dyn Render itself has no fixed compile-time size (dyn Render: !Sized). Attempting to store a bare dyn Render directly in a local variable or pass it by value without indirection fails with E0277:
// ❌ does not compile
trait Render {
fn render(&self) -> String;
}
fn show(item: dyn Render) {
println!("{}", item.render());
}
fn main() {}
error[E0277]: the size for values of type `(dyn Render + 'static)` cannot be known at compilation time
--> src/main.rs:6:15
|
6 | fn show(item: dyn Render) {
| ^^^^^^^^^^ doesn't have a size known at compile time
|
= help: the trait `Sized` is not implemented for `(dyn Render + 'static)`
= help: unsized fn params are gated as an unstable feature
help: function arguments must have a statically known size, borrowed types always have a known size
|
6 | fn show(item: &dyn Render) {
| +
Creating Trait Objects via Unsize Coercion
You create a trait object by taking a reference or smart pointer to a concrete type (&Concrete or Box<Concrete>) and letting the compiler perform an implicit unsize coercion to &dyn Trait or Box<dyn Trait> (or using an explicit as &dyn Trait cast):
trait Codec {
fn name(&self) -> &str;
fn encode(&self, input: &[u8]) -> Vec<u8>;
}
struct IdentityCodec;
impl Codec for IdentityCodec {
fn name(&self) -> &str {
"identity"
}
fn encode(&self, input: &[u8]) -> Vec<u8> {
input.to_vec()
}
}
struct XorCodec {
key: u8,
}
impl Codec for XorCodec {
fn name(&self) -> &str {
"xor"
}
fn encode(&self, input: &[u8]) -> Vec<u8> {
input.iter().map(|b| b ^ self.key).collect()
}
}
fn run_codec(codec: &dyn Codec, payload: &[u8]) {
let encoded = codec.encode(payload);
println!("{:<8} -> {:02X?}", codec.name(), encoded);
}
fn main() {
let id = IdentityCodec;
let xor = XorCodec { key: 0xFF };
// Implicit unsize coercion from `&IdentityCodec` and `&XorCodec` to `&dyn Codec`:
run_codec(&id, &[0x01, 0x02, 0x03]);
run_codec(&xor, &[0x01, 0x02, 0x03]);
}
identity -> [01, 02, 03]
xor -> [FE, FD, FC]
Downcasting Trait Objects with std::any::Any
Once a concrete type is coerced into &dyn Trait, its concrete type identity is erased. If you need to recover the original concrete type at runtime, bound your trait by the std::any::Any supertrait (trait MyTrait: Any) on 'static types and use <dyn Any>::downcast_ref::<ConcreteType>().