Ephemeral (or "weak") closures

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));
}

(Playground)

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 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.

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: