Trait Objects and Dynamic Dispatch

dyn Trait

The dyn keyword prefixes a trait name (dyn Trait) to denote a trait object type—a Dynamically Sized Type (DST) whose concrete underlying type is erased at compile time and resolved via dynamic dispatch at runtime.

Why dyn Exists in Type Syntax

In Rust 2015, trait objects were written as bare trait names (Box<Read>, &Display), which made it visually ambiguous whether a type name in a signature referred to a concrete struct/enum or a dynamically dispatched trait object. The dyn keyword makes dynamic dispatch explicit at the type level (and bare trait objects without dyn are a hard error in Rust 2021 and 2024 editions).

Syntax Meaning Dispatch Mechanism
impl Trait (in parameter/return position) Opaque compile-time concrete type Static dispatch (monomorphized)
dyn Trait (behind &, &mut, Box, Arc) Type-erased runtime trait object Dynamic dispatch (vtable pointer)

Combining dyn Trait with Auto Traits and Lifetime Bounds

A dyn Trait type can be combined with auto traits (Send, Sync, Unpin) using +, as well as a trait object lifetime bound (+ 'a or + 'static):

// A boxed trait object that can be sent across threads and holds no non-'static borrows:
type BoxedJob = Box<dyn FnOnce() + Send + 'static>;

Note that you cannot combine two arbitrary non-auto traits in dyn TraitA + TraitB (E0225). If you need a trait object that provides methods from both TraitA and TraitB, define a combined subtrait (trait Combined: TraitA + TraitB {}) with a blanket implementation and use dyn Combined:

use std::fmt::{Debug, Display};

trait PrintableDebug: Display + Debug {}
impl<T: Display + Debug> PrintableDebug for T {}

fn inspect_item(item: &dyn PrintableDebug) {
    println!("Display: {item} | Debug: {item:?}");
}

fn main() {
    let number = 42;
    let text = "status_ok";

    inspect_item(&number);
    inspect_item(&text);
}
Display: 42 | Debug: 42
Display: status_ok | Debug: "status_ok"

Default Trait Object Lifetime Elision Rules

Every dyn Trait has an implicit lifetime bound indicating how long any references stored inside the underlying concrete type are valid:

  • Box<dyn Trait> defaults to Box<dyn Trait + 'static>.
  • &'a dyn Trait defaults to &'a (dyn Trait + 'a).
  • &'a mut dyn Trait defaults to &'a mut (dyn Trait + 'a).

If a Box<dyn Trait> wraps a struct that borrows data for a shorter lifetime 'a, write the lifetime bound explicitly as Box<dyn Trait + 'a>.