]> git.lizzy.rs Git - rust.git/blob - src/doc/tarpl/phantom-data.md
034f31784295df76ba3fdc05b2e9bd40cee91fe3
[rust.git] / src / doc / tarpl / phantom-data.md
1 % PhantomData
2
3 When working with unsafe code, we can often end up in a situation where
4 types or lifetimes are logically associated with a struct, but not actually
5 part of a field. This most commonly occurs with lifetimes. For instance, the
6 `Iter` for `&'a [T]` is (approximately) defined as follows:
7
8 ```rust,ignore
9 struct Iter<'a, T: 'a> {
10     ptr: *const T,
11     end: *const T,
12 }
13 ```
14
15 However because `'a` is unused within the struct's body, it's *unbounded*.
16 Because of the troubles this has historically caused, unbounded lifetimes and
17 types are *illegal* in struct definitions. Therefore we must somehow refer
18 to these types in the body. Correctly doing this is necessary to have
19 correct variance and drop checking.
20
21 We do this using *PhantomData*, which is a special marker type. PhantomData
22 consumes no space, but simulates a field of the given type for the purpose of
23 static analysis. This was deemed to be less error-prone than explicitly telling
24 the type-system the kind of variance that you want, while also providing other
25 useful such as the information needed by drop check.
26
27 Iter logically contains a bunch of `&'a T`s, so this is exactly what we tell
28 the PhantomData to simulate:
29
30 ```
31 use std::marker;
32
33 struct Iter<'a, T: 'a> {
34     ptr: *const T,
35     end: *const T,
36     _marker: marker::PhantomData<&'a T>,
37 }
38 ```
39
40 and that's it. The lifetime will be bounded, and your iterator will be variant
41 over `'a` and `T`. Everything Just Works.
42
43 Another important example is Vec, which is (approximately) defined as follows:
44
45 ```
46 struct Vec<T> {
47     data: *const T, // *const for variance!
48     len: usize,
49     cap: usize,
50 }
51 ```
52
53 Unlike the previous example it *appears* that everything is exactly as we
54 want. Every generic argument to Vec shows up in the at least one field.
55 Good to go!
56
57 Nope.
58
59 The drop checker will generously determine that Vec<T> does not own any values
60 of type T. This will in turn make it conclude that it does *not* need to worry
61 about Vec dropping any T's in its destructor for determining drop check
62 soundness. This will in turn allow people to create unsoundness using
63 Vec's destructor.
64
65 In order to tell dropck that we *do* own values of type T, and therefore may
66 drop some T's when *we* drop, we must add an extra PhantomData saying exactly
67 that:
68
69 ```
70 use std::marker;
71
72 struct Vec<T> {
73     data: *const T, // *const for covariance!
74     len: usize,
75     cap: usize,
76     _marker: marker::PhantomData<T>,
77 }
78 ```
79
80 Raw pointers that own an allocation is such a pervasive pattern that the
81 standard library made a utility for itself called `Unique<T>` which:
82
83 * wraps a `*const T` for variance
84 * includes a `PhantomData<T>`,
85 * auto-derives Send/Sync as if T was contained
86 * marks the pointer as NonZero for the null-pointer optimization
87