Is there any way to downcast from dyn Trait to the original type for types that don’t implement Any? I know for sure that my value is of the correct type so I don’t really need to check the type ID, and as far as I can tell the rest is just a simple transmute. But I’m assuming that the transmute is an implementation detail that I shouldn’t rely on, is there any official way of doing this?
Completely generically? Not without using unsafe in some way, like a trip through raw pointers. (Which may be unsound depending on the details of the non-'static type in question.)
For a safe way, you might be able to put a method on the trait (or a custom trait) that does the conversion, like some into_widget<'this>(self: Box<Self>) -> Option<Widget<'this>> where Self: 'this or what-have-you.
I’m looking for a completely generic solution, since my trait can be implemented by other people and I need this to work with all of those types. I’m okay with unsafe, what I’m worried about is correctness.
You mean a separate merhod for every possible type that I may need to downcast to? Yeah, that’s not an option.
OK, so you literally are keeping the type information around. The pointer way would be
pub fn get_orig_value_mut(&mut self) -> &mut T {
let ptr = &mut (*self.value)
as *mut (dyn MyTrait + 'a)
as *mut T;
// SAFETY: we are sure the erased type is `T` (modulo variance)
// because......
unsafe { &mut *ptr }
}
I don't see why you just don't store T? I know it's a minimal example, but I'm bringing it up as the correctness (soundness) here depends on being guaranteeing that the type that got erased into the dyn MyTrait + 'a was T (modulo variance). That means you can't expose &mut Box<dyn MyTrait + 'a> for example, or someone could swap it out for a different one. And it means the only (safe) way of constructing Container has to be something like new where you're sure of the erased type.
(If you don't keep the type information around, there is no solution that meets your requirements without making the caller use unsafe (i.e. punting the safety requirements downstream). A type with multiple[1] lifetimes could be coered to dyn MyTrait + 'd, and there's no way to recover that information. So you generally can't actually know what the original type was in that case.)
The downcast_*() methods are provided by Any itself. Unless your trait supplies an (implicit) alternative to them, the question makes little sense. The downcasting done by Any relies on checking against the (compiler's intrinsic) TypeId::of<T>, which in turn requires T: ?Sized + 'static. Meanwhile, your example explicitly limits your type to T: MyTrait + 'a. So: "nope".
@quinedot might have been talking about your own spin on the Any's method/s:
pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
if self.is::<T>() {
// SAFETY: ... (see the docs)
unsafe { Some(self.downcast_ref_unchecked()) }
} else {
None
}
}
What exact ways to validate a type with some non 'static inner references mixed in, would be a whole other question. If you "know for sure" and you "don’t really need to check the type", just do it the "pointer way" as per the answer above. The easiest way, it would be - that's for certain.
There's a crate that provides a non-'staticTypeId style system, that you could trivially extend to provide downcasting. Be warned though, it's pretty complex (I for one can't understand it).
Oh nice, I didn’t know I could simply cast a fat pointer to a slim pointer like that, I guess that answers my question.
That’s a good point, thanks for bringing it up. The dyn side of things is completely under my control so this shouldn’t be an issue, only the &mut T side gets exposed to external users.
I literally keep the type info around in a type param, it’s slightly more complicated than my example but the correctness should be guaranteed by the compiler. So hopefully I won’t need to keep any sort of runtime typeid.
Thank you for the suggestion. I think I should have provided a more complete example to make it clear why I need the dyn MyTrait in the first place. I'm trying to make a heterogeneous collection of items that could all be of different types but all share the same trait, so replacing it with PrivateTrait<T> wouldn't work. Here's a more complete example, now working as intended, let me know if this makes sense, thank you!
I might have been too hasty with that invariant remark. I was thinking along the lines of, it could be a problem if T is allowed variance in the indices independent of the owning container. But the container has no type information,[1] so probably it's fine.
(I haven't had a chance to play with it anymore. If I find another actual problem later I'll post it.)
Edit: It's not fine, see next comment.
other than the dyn lifetime which you don't use in the downcast ↩︎
Oh wow, okay, I didn't know this could happen. So basically what you're saying is that the compiler will analyze the variance of Idx with respect to its type arguments and allow the lifetime to change because of the covariance. Thanks for finding this and showing me how to fix it, you really helped me out a lot!
I don't think it's the cloning that's the problem. The same thing happens even if you don't make any copies of the index at all (and change the index_mut signature to take a reference instead): see here. I think what happens is that index_mut is allowed to take an Idx argument of a different lifetime than its type argument because of covariance (as long as the Idx lifetime is longer).