Arc::downcast without the requirement for Send + Sync

I'm working on an interface that can accept user defined types and I want it to work for both Send + Sync and non-Send+Sync types. And for that I'm using and Arc.

The problem I have is that Arc::downcast Is implemented only for Any + Send + Sync + 'static and I need it for Any + 'static. Here's what I did:

pub fn downcast_arc<T>(arc: Arc<dyn Any>) -> Result<Arc<T>, Arc<dyn Any>>
    where
        T: Any + 'static,
    {
        if (*arc).is::<T>() {
            let ptr = Arc::into_raw(arc).cast::<T>();
            
            Ok(unsafe { Arc::from_raw(ptr) })
        } else {
            Err(arc)
        }
    }

It works, but is this safe?

It is.

  • (As safe as any TypeId-based (down)casting can be: there currently is a 1 in 2^64 that the TypeId hash collides with another type's, which would make Any::is return a false positive).

Indeed, Arc<dyn Any> = (Arc<Erased>, &'static <T as Any>::VTable); thanks to the vtable you get access to .type_id() which lets you "check" whether Erased used to be T. You then drop the vtable reference and cast Erased back to the T it presumably was. In the meantime, Arc::into_raw and Arc::from_raw have been working with pointers to the same data, so all is good.

2 Likes