Downcast_ref() not working on a trait that *does* inherit from Any

I have a trait that is defined like this:

pub trait MyTrait: Any + Send { ... }

Application will implement this trait:

struct MyImplementation;

impl MyTrait for MyImplementation { ... }

...and pass its implementation to my library:

fn register(&mut self, callbacks: impl MyTrait) { ... }

The instance is stored in a Box<dyn MyTrait> inside of my library:

struct MyLibrary {
    registered: Box<dyn MyTrait>,
}

Now, if I later return the Box<dyn MyTrait> back to the application, how I can convert it back to the concrete type? Specifically, how can I get the MyImplementation out of the box?

I don't understand why this does not work:

let my_ref: &dyn MyTrait = boxed_value.as_ref();
let x: Option<&MyImplementation> = my_ref.downcast_ref(); // <-- ERROR

MyTrait inherits from Any, so why I cannot call downcast_ref() on the &dyn MyTrait reference? The method downcast_ref() shall be available in a type that implements Any, which is also true for any type that implements MyTrait, since MyTrait extends Any.


Strange enough, it does work if I do:

let my_ref: &dyn MyTrait = boxed_value.as_ref();
let any_ref = my_ref as &dyn Any;
let x: Option<&MyImplementation> = any_ref.downcast_ref(); // <-- OK

But this requires Rust 1.86 or newer. Older Rust says the cast to &dyn Any is not allowed!

Why is the cast even needed?

And is there any "clean" way to do that in older Rust versions too?

Regards.

There are similar previous discussions: 1, 2.

As for casting from dyn MyTrait to dyn Any, it's a feature called trait upcasting stabilized in edition 2024. I don't know much about that, but here's a previous discussion that might be helpful.

My current solution to this is to use downcast-rs crate.

Edit: Changed the first link

1 Like

downcast_ref is a method implemented on dyn Any, which is a different type from dyn MyTrait. It is not a method on the Any trait itself because downcast_ref is generic and traits with generic methods can't be converted to a trait object.

Too bad. Even an explicity Any::downcast_ref(my_trait_ref) does not work :sad_but_relieved_face:

Also I don't get why types implementing Any don't automatically have an as_any() function.

I have seen suggestions to do something like this:

pub trait AsAny {
    fn as_any(&self) -> &dyn Any;
    fn as_mut_any(&mut self) -> &mut dyn Any;
}

impl<T: Any> AsAny for T {
    fn as_any(&self) -> &dyn Any {
        self
    }

    fn as_mut_any(&mut self) -> &mut dyn Any {
        self
    }
}

...which seems like something that should be there by default.

The usual reason to define as_any() methods was as a workaround for lack of trait upcasting, so they are no longer necessary.

2 Likes

Okay. But that is only with the newest Rust versions, right?

(The project I'm currently working on uses Rust 1.85, which doesn't allow trait upcasting yet)

Yes, of course.

If your question is “why was there never a std trait for as_any() for use in older versions”, well, one thing to notice is that you would need three additional traits — one each for dyn Any + Send, dyn Any + Sync, and dyn Any + Send + Sync. It’s not a simple thing with exactly one correct definition.

1 Like

Yeah, that. And also why it was made so that &dyn references to traits that extend Any must be casted back to &dyn Any explicitely, before we can do the required downcast_ref().

Before trait upcasting was implemented, it would have been impossible to provide such a method on all such &dyn types. Now, it would need a newly introduced trait, again purely for convenience and not for new functionality. (Which isn’t to say that such a thing would necessarily be rejected, but rather that someone would have to implement it — and argue that it’s worth having as an additional feature, not just a natural consequence of other features.)

2 Likes