Advanced Traits

Trait Upcasting

Trait upcasting (stabilized in Rust 1.86) is the implicit or explicit coercion of a trait object pointer (&dyn Subtrait, &mut dyn Subtrait, Box<dyn Subtrait>, Arc<dyn Subtrait>) into a trait object pointer of one of its supertraits (&dyn Supertrait or &dyn Any) without requiring a manual helper method on the trait.

How Vtable Pointer Adjustment Works During Upcasting

When trait Subtrait: Supertrait, the compiler embeds a pointer to Supertrait's vtable inside Subtrait's vtable (or shares a prefix layout when single-inheritance applies). When &dyn Subtrait is upcast to &dyn Supertrait, the data pointer remains unchanged while the fat pointer's vtable word is updated to point to the Supertrait vtable:

Before Upcast: `&dyn Subtrait`
+------------------+-----------------------+
| data_ptr (8 B)   | subtrait_vtable_ptr --+---> [ drop, size, align, super_fn, sub_fn, super_vtable_ptr ]
+------------------+-----------------------+                                                |
                                                                                            v
After Upcast: `&dyn Supertrait`                                                             |
+------------------+-----------------------+                                                |
| data_ptr (8 B)   | super_vtable_ptr -----+------------------------------------------------+
+------------------+-----------------------+

Upcasting to a Supertrait and Downcasting via Any

Because trait Component: Any has std::any::Any as a supertrait, any &dyn Component or Box<dyn Component> can be upcast directly to &dyn Any or Box<dyn Any> and then downcast back to its concrete type using Any::downcast_ref or Any::downcast:

use std::any::Any;

trait Named {
    fn name(&self) -> &str;
}

trait Widget: Named + Any {
    fn draw(&self) -> String;
}

struct Button {
    label: String,
    width: u32,
}

impl Named for Button {
    fn name(&self) -> &str {
        &self.label
    }
}

impl Widget for Button {
    fn draw(&self) -> String {
        format!("[Button: {} ({}px)]", self.label, self.width)
    }
}

fn print_named(item: &dyn Named) {
    println!("Named supertrait view: {}", item.name());
}

fn main() {
    let btn: Box<dyn Widget> = Box::new(Button {
        label: String::from("Submit"),
        width: 120,
    });

    println!("Widget view: {}", btn.draw());

    // 1. Upcast `&dyn Widget` -> `&dyn Named` automatically:
    print_named(&*btn);

    // 2. Upcast `&dyn Widget` -> `&dyn Any` and downcast to `&Button`:
    let as_any: &dyn Any = &*btn;
    if let Some(concrete_btn) = as_any.downcast_ref::<Button>() {
        println!("Downcast back to Button: width = {}px", concrete_btn.width);
    }
}
Widget view: [Button: Submit (120px)]
Named supertrait view: Submit
Downcast back to Button: width = 120px

Dropping Marker Auto-Traits via Trait Upcasting

Trait upcasting also allows dropping marker auto-traits (Send, Sync, Unpin) from a trait object type—for example, coercing Box<dyn Future<Output = ()> + Send> into Box<dyn Future<Output = ()>> with zero allocation or vtable rebuilding.