diff --git a/lifetimes.md b/lifetimes.md index 02e307d57fc6..159a3b00074f 100644 --- a/lifetimes.md +++ b/lifetimes.md @@ -474,16 +474,9 @@ struct Foo<'a, 'b, A, B, C, D, E, F, G, H> { - -## Dropck - -TODO - - ## PhantomData - -However when working with unsafe code, we can often end up in a situation where +When working with unsafe code, we can often end up in a situation where types or lifetimes are logically associated with a struct, but not actually part of a field. This most commonly occurs with lifetimes. For instance, the `Iter` for `&'a [T]` is (approximately) defined as follows: @@ -498,7 +491,8 @@ pub struct Iter<'a, T: 'a> { However because `'a` is unused within the struct's body, it's *unbound*. Because of the troubles this has historically caused, unbound lifetimes and types are *illegal* in struct definitions. Therefore we must somehow refer -to these types in the body. +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 @@ -516,8 +510,50 @@ pub struct Iter<'a, T: 'a> { } ``` -However PhantomData is also necessary to signal important information to -*dropck*. (TODO) + + + +## Dropck + +When a type is going out of scope, Rust will try to Drop it. Drop executes +arbitrary code, and in fact allows us to "smuggle" arbitrary code execution +into many places. As such additional soundness checks (dropck) are necessary to +ensure that a type T can be safely instantiated and dropped. It turns out that we +*really* don't need to care about dropck in practice, as it often "just works". + +However the one exception is with PhantomData. Given a struct like Vec: + +``` +struct Vec { + data: *const T, // *const for covariance! + len: usize, + cap: usize, +} +``` + +dropck will generously determine that Vec does not contain any values of +type T. This will unfortunately allow people to construct unsound Drop +implementations that access data that has already been dropped. In order to +tell dropck that we *do* own values of type T and may call destructors of that +type, we must add extra PhantomData: + +``` +struct Vec { + data: *const T, // *const for covariance! + len: usize, + cap: usize, + _marker: marker::PhantomData, +} +``` + +Raw pointers that own an allocation is such a pervasive pattern that the +standard library made a utility for itself called `Unique` which: + +* wraps a `*const T`, +* includes a PhantomData, +* auto-derives Send/Sync as if T was contained +* marks the pointer as NonZero for the null-pointer optimization +