Upcasting

39. Trait Upcasting — Cast dyn Trait to dyn Supertrait

Since Rust 1.86, you can upcast a trait object to its supertrait — no workarounds needed.

The problem

Imagine you have a trait hierarchy:

1
2
3
4
5
use std::any::Any;

trait Animal: Any {
    fn name(&self) -> &str;
}

Before Rust 1.86, if you had a &dyn Animal, you couldn’t simply cast it to &dyn Any. You’d have to add an explicit method to your trait:

1
2
3
4
5
// The old workaround — adding a method just to upcast
trait Animal: Any {
    fn name(&self) -> &str;
    fn as_any(&self) -> &dyn Any;
}

This was boilerplate that every trait hierarchy had to carry around.

The fix: trait upcasting

Now you can coerce a trait object directly to any of its supertraits:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
use std::any::Any;

trait Animal: Any {
    fn name(&self) -> &str;
}

struct Dog;

impl Animal for Dog {
    fn name(&self) -> &str {
        "Rex"
    }
}

fn print_if_dog(animal: &dyn Animal) {
    // Upcast to &dyn Any — just works!
    let any: &dyn Any = animal;

    if let Some(dog) = any.downcast_ref::<Dog>() {
        println!("Good boy, {}!", dog.name());
    } else {
        println!("Not a dog.");
    }
}

fn main() {
    let dog = Dog;
    print_if_dog(&dog);
}

The key line is let any: &dyn Any = animal; — this coercion from &dyn Animal to &dyn Any used to be a compiler error and now just works.

It works with any supertrait chain

Upcasting isn’t limited to Any. It works for any supertrait relationship:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
trait Drawable {
    fn draw(&self);
}

trait Widget: Drawable {
    fn click(&self);
}

fn draw_it(widget: &dyn Widget) {
    // Coerce &dyn Widget → &dyn Drawable
    let drawable: &dyn Drawable = widget;
    drawable.draw();
}

This makes trait object hierarchies much more ergonomic. No more as_drawable() helper methods cluttering your traits.