Why does `SyncUnsafeCell: Sync` require `T: Sync`?

Sure, this could be a little safer of a road bump, but SyncUnsafeCell is AIUI supposed to say “this place has external synchronization” (where UnsafeCell is “needs”). If we're already promising to use external synchronization, why does this not apply to T: Sync as well?

Furthermore, the main use case for SyncUnsafeCell is to be able to replace uses of static mut: T with static: SyncUnsafeCell<T> instead[1], which is a bit safer. But in a common use case — statics required by C API design — T: Sync would be lying (it's a bag of pointers requiring external synchronization) and the point of not needing a wrapper type is moot.


  1. Although with edition2024 making references of static mut not just unsafe but a hard error, requiring the use of &raw pointers instead, motivation gets a bit weaker; &raw mut PLACE vs PLACE.as_ptr(). ↩︎

3 Likes

In the tracking issue

this is listed as an unresolved question:

  • Should it be Sync only if T is Sync, or should it unconditionally be Sync?

I’ve also come across this limitation in this thread before; comparing to static mut, which doesn’t pose any Sync bounds either.


Also related, an API I had opened afterwards, aiming to replace SyncUnsafeCell entirely.

Which also contains some thoughts about how the T: Sync requirement can be useful, by @mara.

In that ACP issue, I also came across the fact that AtomicPtr is another precedent where raw pointers can be made to cross thread-boundaries easily, without any additional Sync requirements on the pointed-to type, which can be another argument the other way.

2 Likes