Send and Sync impls for Arc

why does Arc<T> requires T: Send + Sync for both Send and Sync?

unsafe impl<T: Send + Sync> Send for Arc<T> {}
unsafe impl<T: Send + Sync> Sync for Arc<T> {}

also, How should one think in general before implementing these marker traits for our own type?

Send for Arc<T> allows moving it to another thread while still shared, and it derefs to &T, so we must have T: Sync for consistent sharing. It also has methods like into_inner returning T if this is the only strong reference, so T: Send is needed to move that across threads.

Sync for Arc<T> allows moving &Arc<T> to another thread and still derefs to &T, so that's T: Sync again. In that new thread you could Arc::clone to get a value again, and if the original drops, then this one could also call into_inner, so we need T: Send.

12 Likes

Here's my goto post on the general topic of Send and Sync. And see also Exclusive.

1 Like

Box<T> derefs to &T as well..why we don't require a T: Sync bound?

We do.

1 Like

ah, I see. So it's like Arc<T> through Deref behaves like an &T and therefore the T: Sync, correct? Then in general this should always be the case for types that do implement a deref, right?

if you can do &MyType<T> -> &T without some sort of exclusive locking, then you must have MyType<T> : Sync => T : Sync. this can be through Deref, but also any other mechanism, like indexing for slices and HashMaps, or enum variants like Option and Result.

cases where this doesn't apply are rare, but include Exclusive (which doesn't allow &Exclusive<T> -> T, Mutex (which uses exclusive locking), and LocalKey (which makes sure each thread can only access it's own elements)

3 Likes

Specifically for the T: Sync requirement for Send for Arc<T>: a Arc<T> that was moved to another thread derefs to &T, while another Arc<T> can still exist on the original thread and also derefs to &T. With Box<T> this cannot happen because there's only one Box<T> that point to that T.

6 Likes