Rc::downcast::<T>(...) only accepts
Rc<Any>. Could it be modified to accept any
Assuming I correctly understand what you’re asking for, the answer is no. Not in any straightforward way. Rust is statically types, and you cannot test whether or not a type implements a trait at runtime, unless you make sure this information is made available somehow. The
Any trait only offers
TypeId which allows comparing individual types for whether or not they are equal, but nothing more. There’s no information about what traits a type implements in a
If you need such downcasting ability in a trait hierarchy, you could consider adding methods such as
fn downcast_ref_to_foo(&self) -> Option<&dyn Foo> (and potentially more for
&mut self or
Box<Self> downcassting, etc.) to a supertrait of
Foo that you’d like to downcast from, and you’ll have to make sure to accurately implement that yourself in order to reflect whether or not
Foo is implemented by a type.
On a second read, you might be asking for something different here, and my previous answer is perhaps what you expected in this following thread, (without explicitly asking for it)
Downcasting is implemented in terms of a method to retrieve a
TypeId, and this is what
Any provides. Downcasting to a type from
Rc<dyn SomeOtherTrait> can’t work for just any other abitrary trait, but if you set up the trait accordingly, it could be feasible. In fact, it is possible to implement other traits that include
Any-like capabilities already: AFAIR crates like mopa or downcast-rs present patterns of how to achieve this.
In this case I'll prefer to not downcast then... I'll just use
Rc<dyn Any> instead of
Rc<dyn DisplayObject> wherever needed.
What you're probably looking for is something like
(NOT A CONTRIBUTION)
It would be trivial to make this something that Rust had out of the box instead of making traits opt into this with "mopa" style hacks.
All you'd need is to include the type id in every vtable just like the size and align already are, and then add a
align_of_val type of function (
TypeId::of_val?) to access that type id for anything that is 'static. Then you could use that to implement downcast on pointers to any trait object. You don't have to restrict it to trait objects, but its useless for anything else, and you could restrict it to trait objects with the Pointee/Metadata/DynMetadata APIs that have already been added.
I had to do the "mopa" trick myself just a few weeks ago. This is the kind of incremental improvement that I wish the project would focus on to make trait objects easier to deal with.
But this isn't zero cost. This grows every vtable by a
u64s at the moment IIRC). If you want to do that, you can do that the way
What I do want to see is some feature to enable
dyn Trait with
const. This will enable more efficient
Any-like conversion (instead of a method, just retrieve a constant). However I'm not sure what shape the feature should have.
In my ideal world I should be able to change a
Rc<dyn Foo> in a
Rc<dyn Foo + Any> and then being able to use
Rc::downcast without additional hassles thanks to trait upcasting automatically coercing the
Rc<dyn Foo + Any> to a
(NOT A CONTRIBUTION).
A type id is one u64; this is a really stretched definition of "not zero cost" when every vtable already contains a constant overhead of 3 usizes (size, align and drop in place) plus a usize for every function in the trait definition.
Supporting proper upcasting is another solution; the key difference is that it if you get a trait object from a library you can't possibly downcast it unless the library has added the
Any bound, whereas it could also just be a feature of trait objects in general.
But the current solution - requiring the trait definer to know about and use a library like mopa or write unsafe code and understand how downcasting is implemented - is really inadequate. The expertise needed to write a downcastable trait object that isn't Any is far too high.
TypeId being a
u64 isn't set in stone; this PR changes it to be a
u64 plus a static reference (and a larger static memory footprint) for example.
I was referring to this:
And this may be important to some people.
Notably every trait being
Any means that the static data cost of
TypeId (a mangled type name string, or perhaps a cryptographic-strong hash of such instead) is paid for every type you take any
dyn rather than just the ones you make
I fully agree that
mopa should be easier to do — it should just be
trait Mine: Any — but since it's required that the RTTI is more than just a
u64 (see discussion about how
TypeId is unsound; we do have collisions in practice), it makes sense to keep it opt in.
I don't do RTTI often so I'm probably missing the context here. Why doesn't
trait Mine: Any just work? I looked at the implementation of
mopa crate and it seems to replace
trait mopa::Any: Any with
TypeId::of as a trait method. Is that the issue? It seems like a simple fix...
mopa::Any trait only exists because
Any::type_id was (differently named and) unstable at the time when
mopa was released / last updated.
The problem why you can’t downcast a
trait Mine: Any is because the
downcast… methods are inherent methods of
dyn Any (with
&mut self receiver) or
Rc<dyn Any> or
Box<dyn Any>, etc.
The macro from
mopa adds analogous methods for
&mut dyn Mine, or
Box<dyn Mine>. There is also ongoing development on trait upcasting, for turning
&dyn Mine into
&dyn Any (or similarly with
&mut … or
Box<…>, etc.) between sub-/super-traits
trait Mine: Any, so you could cast
&dyn Mine to
&dyn Any first and then use regular
downcast_ref. This however doesn’t give the perfect API for something owned like
Box<dyn Mine> because with this approach, you’ll be left with a
Box<dyn Any> in case downcasting fails, with no way to turn it back into
Box<dyn Mine>, whereas
downcast will return
Result<Box<T>, Box<dyn Mine>>.