A static global C string (as in this answer) doesn't have the Sync
trait.
pub static MY_STRING: &'static *const u8
= "hello" as const *u8;
// TODO: Simple assertion showing it's not Sync ;)
Sync
is described as
The precise definition is: a type T
is Sync
if &T
is thread-safe. In other words, there is no possibility of data races when passing &T
references between threads.
It seems like this is entirely readonly and has static lifetime, so why isn't it safe to pass a reference?
The chapter Send and Sync in The Rustonomicon describes what it means for a type to be Send
or Sync
. It mentions that:
- raw pointers are neither Send nor Sync (because they have no safety guards).
But that just begs the question; why doesn't *const T
implement Sync
? Why do the safety guards matter?
Just before that, it says:
Send and Sync are also automatically derived traits. This means that, unlike every other trait, if a type is composed entirely of Send or Sync types, then it is Send or Sync. Almost all primitives are Send and Sync, and as a consequence pretty much all types you'll ever interact with are Send and Sync.
This is the key reason why raw pointers are neither Send
nor Sync
. If you defined a struct that encapsulates a raw pointer, but only expose it as a &T
or &mut T
in the struct's API, did you really make sure that your struct respects the contracts of Send
and Sync
? If raw pointers were Send
, then Rc<T>
would also be Send
by default, so it would have to explicitly opt-out. (In the source, there is in fact an explicit opt-out for Rc<T>
, but it's only for documentation purposes, because it's actually redundant.)
[...] they're unsafe traits. This means that they are unsafe to implement, and other unsafe code can assume that they are correctly implemented.
OK, let's recap: they're unsafe to implement, but they're automatically derived. Isn't that a weird combination? Actually, it's not as bad as it sounds. Most primitive types, like u32
, are Send
and Sync
. Simply compounding primitive values into a struct or enum is not enough to disqualify the type for Send
or Sync
. Therefore, you need a struct or enum with non-Send
or non-Sync
before you need to write an unsafe impl
.
Send
and Sync
are marker traits, which means they have no methods. Therefore, when a function or type puts a Send
or Sync
bound on a type parameter, it's relying on the type to respect a particular contract across all of its API. Because of this:
Incorrectly implementing Send or Sync can cause Undefined Behavior.