Rc<Any> -> Option<Rc<AnimalT>>

use std::rc::Rc;
use core::any::Any;



pub trait AnimalT {}


fn foo (ptr: Rc<Any>) -> Option<Rc<AnimalT>> {
    ptr.downcast::<AnimalT>().ok()

}

gets error:

error: the `downcast` method cannot be invoked on a trait object
   --> src/lib.rs:10:9
    |
10  |     ptr.downcast::<AnimalT>().ok()
    |         ^^^^^^^^

error: aborting due to previous error; 3 warnings emitted

error: could not compile `playground`.

Is Rc<Any> -> Option<Rc<AnimalT>> possible at all?

No you can't. Traits are not type, they're abstract behavior shared between types. dyn Trait is a dynamically sized type whose reference also holds a ptr to the type-specific vtable which contains the function pointers for the trait.

Problem is, there's no way to get the vtable ptr of trait AnimalT from the dyn Any type. dyn Any's vtable holds a type id of the underlying concrete type, which is a hash. You can get the type id from the concrete type and compare it with the one from dyn Any, but you can't get type from the type id.

2 Likes

Remember to use the dyn Trait syntax. Otherwise you risk confusing us. Your syntax is deprecated for a reason.

2 Likes

You’ll need to keep track of all the different AnimalT types yourself:

use std::rc::Rc;
use core::any::{Any,TypeId};
use std::collections::HashMap;

#[derive(Default)]
struct AnimalRegistry ( HashMap<TypeId, fn(Rc<dyn Any>)->Rc<dyn AnimalT>> );

impl AnimalRegistry {
    pub fn register<T:AnimalT+'static>(&mut self) {
        self.0.insert(
            TypeId::of::<T>(),
            |any| any.downcast::<T>().unwrap()
        );
    }

    pub fn as_dyn_animal(&self, x:Rc<dyn Any>)->Option<Rc<dyn AnimalT>> {
        self.0.get(&x.type_id()).map(move |f| f(x))
    }
}
1 Like

I can't seem to do

use std::rc::Rc;
use core::any::Any;

pub trait AnimalT {
    fn maybe_dog(self: &Rc<Self>) -> Option<Rc<dyn DogT>> {
        None
    }
    
}

pub trait DogT: AnimalT {
    fn maybe_dog(self: &Rc<Self>) -> Option<Rc<dyn DogT>> {
        Some(self.clone())
    }
}

pub trait CatT: AnimalT {}

fn main() {
    
}

either. Is there no choice besides enumerating all the concrete types ?

When you control the parent trait it is possible, but you can't automate implementing maybe_dog. Every implementor of AnimalT must have a separate implementation of maybe_dog.

1 Like

Generally, Rc arguments are owned, rather than passed as a reference. If you make that change, your code will compile but not behave as you expect (per @alice's comment).

It is possible to define a helper macro to make the individual implementations easier, though:

pub trait AnimalT {
    fn maybe_dog(self: Rc<Self>) -> Option<Rc<dyn DogT>> {
        None
    }
    
    fn maybe_cat(self: Rc<Self>) -> Option<Rc<dyn CatT>> {
        None
    }
}

macro_rules! animal_subtrait_impl {
    (DogT) => {
        fn maybe_dog(self: Rc<Self>) -> Option<Rc<dyn DogT>> {
            Some(self.clone())
        }
    };
    
    (CatT) => {
        fn maybe_cat(self: Rc<Self>) -> Option<Rc<dyn CatT>> {
            Some(self.clone())
        }
    };

    ($($subtrait:tt)*) => {
        $( animal_subtrait_impl!{$subtrait} )*
    }
}

pub trait DogT: AnimalT {}
pub trait CatT: AnimalT {}

struct Terrier;

impl AnimalT for Terrier {
    animal_subtrait_impl!{DogT}
}
impl DogT for Terrier {}
1 Like