Closed lilyball closed 1 year 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.
According to the
PhantomData
patterns table, there seems to be no difference between using e.g.*const T
andfn() -> T
. Both are covariant onT
and have no lifetime. But there actually is a difference, which is thatPhantomData<*const T>
is notSend
orSync
, whereasPhantomData<fn() -> T>
isSend
andSync
.This is something that users can figure out by thinking about how
PhantomData
implements auto traits according to itsT
parameter, but this isn't explicitly called out in the nomicon (or in thestd::marker::PhantomData
docs) and so it's easy to forget. For example, I changed some code fromPhantomData<T>
toPhantomData<fn() -> T>
specifically to avoid the drop check (my type doesn't own aT
, but instead it can produceT
s). It just so happens that this makes my typeSend
andSync
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 includingSend
/Sync
columns in the table that lists whether the given pattern isSend
/Sync
. This would look something like'a
T
Send
Sync
PhantomData<T>
T: Send
T: Sync
PhantomData<&'a T>
T: Sync
T: Sync
PhantomData<&'a mut T>
T: Send
T: Sync
PhantomData<*const T>
PhantomData<*mut T>
PhantomData<fn(T)>
Send
Sync
PhantomData<fn() -> T>
Send
Sync
...