pub trait AnimalT {}
pub struct Cat {}
pub struct Dog {}
foo(x: Rc<dyn AnimalT>) -> Option<Rc<Cat>> {
// I thought this is called a 'dynamic cast'
// but I can't find sample code for it
}
How do we, dynamically, at run time, say: see if we can cast this AnimalT into a Cat ?
Not without a lot of trouble (and maybe some unsafe). All of the downcast methods work on references to dyn Any objects, so you need to provide a way to get one of those from a dyn AnimalT. If you try to do it directly, you’ll run into problems with Sized, I think.
If you get rid of the helper trait, you have to define the to_any method on every implementor of the trait, rather than as a single blanket impl. It's cumbersome, but would work.
Thanks, there is still something I do not understand:
use std::rc::Rc;
use core::any::Any;
pub trait Reinterpret<T: ?Sized> {
fn cast_rc(self: Rc<Self>) -> Rc<T>;
}
impl<T: Any> Reinterpret<dyn Any> for T {
fn cast_rc(self: Rc<Self>) -> Rc<dyn Any> {
self
}
}
pub trait AnimalT: Reinterpret<dyn Any> {}
pub struct Cat {}
impl AnimalT for Cat {}
fn main () {
let x = Rc::new(Cat {});
let y = x as Rc<dyn AnimalT>;
let z = y.cast_rc();
let z_bad = y as Rc<dyn Any>;
}
What is the difference between the "y.cast_rc()" and "y as Rc" so that the former works but the latter fails ?
as can only translate from a concrete type to a trait object, and never from a trait object.
What we’re doing with the trait is defining a method on each of the concrete types that does an as cast. Those methods then get stored in the virtual method table when the dyn AnimalT is constructed so that the compiler knows which one to call in a dynamic context.
The built-in way to perform it is through the Any trait.
For technical reasons, only an exact dyn Any can be downcasted: if you have a "sub"-trait Animal : Any, there is no implicit conversion / coercion from dyn Animal to dyn Any, not even behind pointers.
Such coercion is called upcasting, and despite it not being built-in, there is a trick to have it: bundle the upcasting logic within the "sub"-trait as helper methods (e.g., cast_rc, etc.), and now you can upcast.
So we'd like to write:
trait Animal : Any {
/* `Animal`-specific methods */
/// Provide the default obvious implementation
fn cast_rc (self: Rc<Self>) -> Rc<dyn Any>
{
self as Rc<dyn Any> /* ...? */
}
}
But another technical limitation does not let us write this that simply:
to coerce self: Rc<Self> to a Rc<dyn Any>, we need Self to be a concrete Sized type:
fn cast_rc (self: Rc<Self>) -> Rc<dyn Any>
+ where
+ Self : Sized,
{
self as Rc<dyn Any>
}
to have cast_rc be callable / available on self: Rc<dyn Animal>, we cannot have a where Self : Sized bound:
The general solution for this, in the future, will be partial impls, misnomed / miswritten as default impl, and misplaced inside the specialization feature: