rust-lang / nomicon

The Dark Arts of Advanced and Unsafe Rust Programming
https://doc.rust-lang.org/nomicon/
Apache License 2.0
1.75k stars 258 forks source link

PhantomData patterns table should include interactions with `Send`/`Sync` #320

Closed lilyball closed 1 year ago

lilyball commented 2 years ago

According to the PhantomData patterns table, there seems to be no difference between using e.g. *const T and fn() -> T. Both are covariant on T and have no lifetime. But there actually is a difference, which is that PhantomData<*const T> is not Send or Sync, whereas PhantomData<fn() -> T> is Send and Sync.

This is something that users can figure out by thinking about how PhantomData implements auto traits according to its T parameter, but this isn't explicitly called out in the nomicon (or in the std::marker::PhantomData docs) and so it's easy to forget. For example, I changed some code from PhantomData<T> to PhantomData<fn() -> T> specifically to avoid the drop check (my type doesn't own a T, but instead it can produce Ts). It just so happens that this makes my type Send and Sync too, which it should have been in the first place but nobody realized that until well after the change.

This could be improved by updating the Nomicon to call out the fact that PhantomData will implement auto traits according to its type parameter, and to give an example of this by including Send/Sync columns in the table that lists whether the given pattern is Send/Sync. This would look something like

Phantom type 'a T Send Sync
PhantomData<T> - covariant (with drop check) T: Send T: Sync
PhantomData<&'a T> covariant covariant T: Sync T: Sync
PhantomData<&'a mut T> covariant invariant T: Send T: Sync
PhantomData<*const T> - covariant - -
PhantomData<*mut T> - invariant - -
PhantomData<fn(T)> - contravariant Send Sync
PhantomData<fn() -> T> - covariant Send Sync

...

JanBeh commented 2 years ago

I also got confused by the table. Since it explicitly mentions the difference regarding "drop check", I assumed there would be no other differences. Side note: I think "drop check" only applies when you use #[may_dangle], which also confused me. See also this thread and this other thread.