What unsoundness can be caused by destructor without PhantomData?

You can read this prior thread.

In summary:

  • It used to work in the dangerous way
  • It hasn't since RFC 1238 in most cases, but
    • It's still true if you use the unstable #[may_dangle] (RFC 1327)
    • And probably it will stay that way, but
    • The RFCs left it open to revisit and that documentation update is rather new...
      • ...and the nomicon is explicitly non-normative, and the change was accepted with zero input (much less an FCP) from the Rust teams, so :man_shrugging:

Although unnecessary today, it doesn't hurt, so I still say throw it in your struct if the old wording applies to you.

(But what I really want is an official statement on the matter confirming the updated Nomicon. And what I really really want is for Rust to have a specification.)


So what's the unsafe part? Before the change, a destructor of a type with a generic type parameter was allowed to run after the type was invalid (i.e. when the generic type held a lifetime, the destructor could run after the lifetime "expired"). There were guard-rails: If the Drop implementation had a trait bound on the generic parameter, and the trait bound exposed any method, the loosening of the rules was not allowed (the outer type had to outlive it's lifetime-carrying type parameter; the outer type was considered to own some instance of the type parameter; the drop had to run before the lifetime became invalid).

Unfortunately, the guard-rails were not enough. They are also not sufficient in the face of specialization, should that ever stabilize. So currently, the presence of the type parameter signals ownership... unless you use #[may_dangle].

It's still important in that case as #[may_dangle] doesn't even have the not-fully-effective guard-rails.

And now the only way you can drop after your lifetimes expire is if you use #[may_dangle] to signal that you promise not to look at what you're dropping. And if what you're dropping doesn't have drops of it's own, that's great! But if it does, you need the PhantomData to signal that you "own" some of the generic parameter -- when you drop, some of the parameter will be dropped too. This allows dropck to correctly determine when it's sound for your type to drop or not.

A full example that was linked to in the other thread.

9 Likes