Casting Arc<Mutex<Struct>> to Arc<Mutex<dyn Trait>>

Hi,
I don't understand when an Arc<Mutex<Struct>> can be casted to an Arc<Mutex<dyn Trait>>.
Take a look at this example: Rust Playground

By invocating take my A<M<Struct>> can be interpreted as A<M<dyn Trait>>, but inside an method I can't return A<M<Struct>> as <A<M<dyn Trait>> (see create2).

I'd like to understand why.

The convertion can work, but only for Arc<Mutex> directly, not for &Arc<Mutex>. Is there any reason you're using &Arc? (&'static Arc is a very weird type). Using Arc directly works:

fn create() -> Arc<Mutex<dyn General>> {
    Arc::new(Mutex::new(Explicit {}))
}

If you're interested in details, Container cheet shield may be helpful:

  • Arc<Mutex<Explicit>>Arc<Mutex<dyn General>> requires just changing ptr to (ptr, vtable) pair – the ptr stays the same.
  • &Arc<Mutex<Explicit>>&Arc<Mutex<dyn General>> would need creating new fat arc somewhere (but where?), and then creating a whole new reference to the new arc.
3 Likes

Formally, this is represented by the nightly traits Unsize and CoerceUnsized. Effectively, iff T: Unsize<U> (i.e., a reference to T can be coerced into a reference to U), then Container<T>: CoerceUnsized<Container<U>> for all compatible smart pointers, including & and Arc.

In this case, we have Explicit: Unsize<dyn General>. Since an M<T> directly stores its T in an UnsafeCell at the end, we have M<Explicit>: Unsize<M<dyn General>>, and consequently, A<M<Explicit>>: CoerceUnsized<A<M<dyn General>>>. Thus, we can coerce an A<M<Explicit>> to an A<M<dyn General>>.

However, this does not work for &A<M<Explicit>>&A<M<dyn General>>. As a consequence of the semantics of CoerceUnsized, this coercion would require A<M<Explicit>>: Unsize<A<M<dyn General>>>. However, an Arc doesn't store its value directly, but instead through a pointer. This means that an Arc with a sized value can't be trivially represented an Arc with an unsized value; as @krdln notes, its internal pointer must be converted into a fat pointer. Therefore, even though we have A<M<Explicit>>: CoerceUnsized<...>, we do not have A<M<Explicit>>: Unsize<...>, and we cannot unsize an &A<M<Explicit>>.

2 Likes