Oh I see. I just made myself a small example to see what happens:
use std::marker::PhantomData;
use std::rc::Rc;
struct SendAndSync<T: ?Sized>(PhantomData<fn() -> T>);
struct MaybeSendAndSync<T: ?Sized>(PhantomData<T>);
trait Dropable {}
impl<T: ?Sized> Dropable for T {}
fn main() {
let _b1: Box<dyn Send + Sync> = Box::new(SendAndSync::<Rc<i32>>(PhantomData));
let _b2: Box<dyn Dropable> = Box::new(MaybeSendAndSync::<Rc<i32>>(PhantomData));
// The following won't work:
// let _b3: Box<dyn Send + Sync> = Box::new(MaybeSendAndSync::<Rc<i32>>(PhantomData));
}
I didn't realize that, as it is not mentioned in the table of PhantomData
patterns in the Nomicon. However, a bit more up, it says:
Raw pointers that own an allocation is such a pervasive pattern that the standard library made a utility for itself called
Unique<T>
which:
- wraps a
*const T
for variance- includes a
PhantomData<T>
- auto-derives
Send
/Sync
as if T was contained- marks the pointer as
NonZero
for the null-pointer optimization
But the auto-derivation on Send
/ Sync
is only mentioned in regard to Unique<T>
(which appears to have been removed from std
anyway), so perhaps that section should get an update to explicitly mention all effects of marking a type as being contained (opposed to using fn() -> T
), and not just the drop check part. Or is that "obvious", and/or I just missed it somewhere else?
I haven't considered drop check at all.
I didn't do it in-depth either, just had some shallow thoughts about it (as cited in my previous reply). This stuff still feels so complex that it's hard for me to make a judgement I feel safe with.
I'm not even entirely sure I'm aware of any effects on drop check at all that a
PhantomData
field could have in any case. You linked the table that claims some relevance... feel free to also link me to any additional information or example that demonstrates some real effects on drop check thatPhantomData<T>
fields can have.
The section on PhantomData
in the Nomicon (same page as the table, but more up) mentions the relevance of PhantomData
for drop checking:
Because of the troubles this has historically caused, unbounded lifetimes and types are forbidden in struct definitions. Therefore we must somehow refer to these types in the body. Correctly doing this is necessary to have correct variance and drop checking.
We do this using
PhantomData
, which is a special marker type.PhantomData
consumes no space, but simulates a field of the given type for the purpose of static analysis. This was deemed to be less error-prone than explicitly telling the type-system the kind of variance that you want, while also providing other useful things such as the information needed by drop check.
Other links that might be helpful: