Search code examples
rustunsafe

Downcasting Rc<A> to Rc<B> if A == B


I would like to know if the following code is a valid use (maybe there is a builtin/safe way to downcast Rc<dyn SomeTrait> to Rc<SomeType>? I couldn't find any) and most importantly, is it safe?

use std::any::*;
use std::rc::*;

// NOTE: apparently adding Any here is REQUIRED
//       otherwise it doesn't work at all,
//       I have no idea why
trait SomeTrait: Any {}
struct SomeType;

impl SomeTrait for SomeType {}

fn rc_downcaster<A: 'static, B: ?Sized + 'static>(b: Rc<B>) -> Option<Rc<A>> {
    if Any::type_id(&*b) == TypeId::of::<A>() {
        Some(unsafe {
            let p = Rc::into_raw(b);
            Rc::from_raw(p as *const A)
        })
    } else {
        None
    }
}


fn main() {
    let x: Rc<dyn SomeTrait> = Rc::new(SomeType);
    let _: Rc<SomeType> = rc_downcaster(x).unwrap();
}

Playground


Solution

  • Without using nightly, I found this safe alternative solution:

    use std::any::*;
    use std::rc::*;
    
    trait SomeTrait: AsAny {}
    struct SomeType;
    
    impl SomeTrait for SomeType {}
    
    trait AsAny {
        fn as_any(self: Rc<Self>) -> Rc<dyn Any>;
    }
    
    impl<T: 'static> AsAny for T {
        fn as_any(self: Rc<Self>) -> Rc<dyn Any> where Self: Sized
        {
            self
        }
    }
    
    fn main() {
        let x: Rc<dyn SomeTrait> = Rc::new(SomeType);
        let x: Rc<dyn Any> = x.as_any();
        let _: Rc<SomeType> = Rc::downcast(x).unwrap();
    }
    

    But it is worth mentioning the solution of @cafce25 (in comment):

    You can use Rc::downcast if you are on nightly and add #![feature(trait_upcasting)]. Since that works your code should be sound as well. Playground