The idea is that I can stash a value of type T that implements Display and Debug as Datum::Other(Rc::new(value)); that allows Datum to be efficiently cloneable without duplicating data. But then I want to be able to downcast and return it as an Rc<T>:
The outer Rc is so that the compiler knows that Datum::Other(Rc<MyAny>) is cloneable, and the inner so that I can downcast the MyAny to Rc<T>; then I can cheaply clone and return the Rc<T>.
It works, but I can't help thinking there's a better way to do it.
OK, I hadn't seen the Rc::downcast method. However, what I've got is an Rc<MoltAny>, not a normal Rc<Any>. I'm using this call:
let result = (*other).downcast::<T>();
And I'm getting this compiler error.
no method named `downcast` found for type `std::rc::Rc<(dyn value10::MoltAny + 'static)>` in the current scope
It seems like maybe I could implement the downcast method for MoltAny, but I'm unsure how to do that (tried a couple of things; but I've not fully grokked traits yet). Any ideas?
I also tried removing MoltAny and just using Any; the downcast works then (or, at least, it compiles); but I need Rc<MoltAny>::to_string, which I lose if I just use Any.
I think you will have to use some unsafe to do this, you can use Rc::downcast as a reference for what to do. Btw, if you click the [src] links in the docs it will take you to the source code!
[Meta] I feel that way about many of the more esoteric (to me) techniques that I see in this forum. For me, each such example shows both the power of Rust and the difficulty of really mastering the language.
if let Datum::Other(other) = &*data_ref {
// other is an &Rc<MoltAny>
// Was: let result = (*other).downcast::<T>();
let result = other.clone().downcast::<T>();
if result.is_ok() {
// Let's be sure we're really getting what we wanted.
// Was: let out: Rc<T> = result.unwrap().clone();
let out: Rc<T> = result.unwrap();
return Some(out);
}
}
Before I was trying (*other).downcast::<T>(); but your example code expects to take ownership of the Rc<MoltAny>, so I had to clone it first. But then I didn't need to clone the downcast value before returning it, so I call that a big win.
if result.is_ok() {
// Let's be sure we're really getting what we wanted.
// Was: let out: Rc<T> = result.unwrap().clone();
let out: Rc<T> = result.unwrap();
return Some(out);
}
That's not really an upcast, and it relies on the user to give the default implementation instead of some other implementation. It also only works for the smart pointers that you specify, part of the reason it isn't really an upcast.
Going from T where T: Trait + ?Sized to dyn Trait behind any smart pointer that implements CoerceUnsized/DispatchFromDyn is an upcast in Rust. Note that for now, we can't actually upcast unsized values, like [T] or str. So, what you showed was not an upcast from dyn MoltAny to dyn Any, but calling a method which upcasted the inner type.
This is a common workaround to actual upcasting. We even see the OP show it in the original post. The problem with this approach is that you can't control the implemention of into_any_rc, so the implementor could use any implementation. This can be solved by adding a new trait, but now you have a new trait to keep track of.
Either way, going from Rc<dyn MoltAny> to Rc<dyn Any> isn't possible directly in the same way as going from Rc<i32> to Rc<dyn Any> is. You need to build some glue code to make it work.
Is harder than you think, especially when you want multiple parents and uou want to coerce to any of them. Then when you throw dylibs into the mix it becomes a mess.
The question of upcasting appears regularly on this forum. The thread about my question answers some questions as well: Convert Box<dyn T> to Box<dyn Any>
Bottom line is: theoretically, this is possible. However, the details have not been settled yet and I believe that this will be part of the language in a few years.
The confusion about this topic comes from the fact that upcasting is a trivial operation in oop languages and is reqired for them to be useful. In rust, this is not the case.