Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

rust

unsafe

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

like image 967
Cecile Avatar asked Aug 31 '25 17:08

Cecile


1 Answers

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

like image 154
Cecile Avatar answered Sep 04 '25 10:09

Cecile