To summarize:
-
You wanted to perform downcasting.
-
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:
fn cast_rc (self: Rc<Self>) -> Rc<dyn Any>
- where
- Self : Sized,
- {
- self as Rc<dyn Any>
- }
+ ;
The general solution for this, in the future, will be partial impl
s, misnomed / miswritten as default impl
, and misplaced inside the specialization
feature:
trait Animal : Any {
/* Animal methods */
fn cast_rc (self: Rc<Self>) -> Rc<dyn Any>
;
}
partial
impl<T /* : Sized */> Animal for T {
fn cast_rc (self: Rc<Self>) -> Rc<dyn Any>
{
self as Rc<dyn Any>
}
}
Current specialization
syntax
+ #![feature(specialization)]
+
trait Animal : Any {
/* Animal methods */
fn cast_rc (self: Rc<Self>) -> Rc<dyn Any>
;
}
- partial
+ default
impl<T /* : Sized */> Animal for T {
fn cast_rc (self: Rc<Self>) -> Rc<dyn Any>
{
self as Rc<dyn Any>
}
}
On stable Rust, this cannot be done directly, but can be achieved through a helper trait, as showcased by @alice and @2e71828.
Alternative solution
If you only want to downcast for a very specific type, you can drop the whole Any
ordeal altogether:
trait Animal {
/* Animal methods */
fn try_downcast_cat_rc (self: Rc<Self>)
-> Result<Rc<Cat>, Rc<Self>>
{
// default impl: Downcast fails.
Err(self)
}
}
and then, for the impl Animal for Cat
block, you add:
impl Animal for Cat {
/* Animal methods */
+
+ fn try_downcast_cat_rc (self: Rc<Self>)
+ -> Result<Rc<Cat>, Rc<Self>>
+ {
+ // `Cat` impl: downcast trivially succeeds.
+ Ok(self)
+ }
}
This will allow you to write:
let animal: Rc<dyn Animal> = ...;
if let Ok(cat) = animal.try_downcast_cat_rc() {
...
}
With no Any
, helper trait, upcsting, involved whatsoever
(but this of course only works with a finite set of downcast target types).