]>
Commit | Line | Data |
---|---|---|
8bb4bdeb | 1 | # PhantomData |
c1a9b12d SL |
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 | ||
48663c56 | 8 | ```rust,compile_fail |
c1a9b12d SL |
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*. | |
136023e0 XL |
16 | [Because of the troubles this has historically caused][unused-param], |
17 | unbounded lifetimes and types are *forbidden* in struct definitions. | |
18 | Therefore we must somehow refer to these types in the body. | |
19 | Correctly doing this is necessary to have correct variance and drop checking. | |
20 | ||
21 | [unused-param]: https://rust-lang.github.io/rfcs/0738-variance.html#the-corner-case-unused-parameters-and-parameters-that-are-only-used-unsafely | |
c1a9b12d SL |
22 | |
23 | We do this using `PhantomData`, which is a special marker type. `PhantomData` | |
24 | consumes no space, but simulates a field of the given type for the purpose of | |
25 | static analysis. This was deemed to be less error-prone than explicitly telling | |
26 | the type-system the kind of variance that you want, while also providing other | |
48663c56 | 27 | useful things such as the information needed by drop check. |
c1a9b12d SL |
28 | |
29 | Iter logically contains a bunch of `&'a T`s, so this is exactly what we tell | |
136023e0 | 30 | the `PhantomData` to simulate: |
c1a9b12d | 31 | |
450edc1f | 32 | ```rust |
c1a9b12d SL |
33 | use std::marker; |
34 | ||
35 | struct Iter<'a, T: 'a> { | |
36 | ptr: *const T, | |
37 | end: *const T, | |
38 | _marker: marker::PhantomData<&'a T>, | |
39 | } | |
40 | ``` | |
41 | ||
42 | and that's it. The lifetime will be bounded, and your iterator will be variant | |
43 | over `'a` and `T`. Everything Just Works. | |
44 | ||
45 | Another important example is Vec, which is (approximately) defined as follows: | |
46 | ||
450edc1f | 47 | ```rust |
c1a9b12d SL |
48 | struct Vec<T> { |
49 | data: *const T, // *const for variance! | |
50 | len: usize, | |
51 | cap: usize, | |
52 | } | |
53 | ``` | |
54 | ||
5bcae85e SL |
55 | Unlike the previous example, it *appears* that everything is exactly as we |
56 | want. Every generic argument to Vec shows up in at least one field. | |
c1a9b12d SL |
57 | Good to go! |
58 | ||
59 | Nope. | |
60 | ||
5bcae85e | 61 | The drop checker will generously determine that `Vec<T>` does not own any values |
c1a9b12d SL |
62 | of type T. This will in turn make it conclude that it doesn't need to worry |
63 | about Vec dropping any T's in its destructor for determining drop check | |
64 | soundness. This will in turn allow people to create unsoundness using | |
65 | Vec's destructor. | |
66 | ||
67 | In order to tell dropck that we *do* own values of type T, and therefore may | |
136023e0 | 68 | drop some T's when *we* drop, we must add an extra `PhantomData` saying exactly |
c1a9b12d SL |
69 | that: |
70 | ||
450edc1f | 71 | ```rust |
c1a9b12d SL |
72 | use std::marker; |
73 | ||
74 | struct Vec<T> { | |
0531ce1d | 75 | data: *const T, // *const for variance! |
c1a9b12d SL |
76 | len: usize, |
77 | cap: usize, | |
78 | _marker: marker::PhantomData<T>, | |
79 | } | |
80 | ``` | |
81 | ||
82 | Raw pointers that own an allocation is such a pervasive pattern that the | |
83 | standard library made a utility for itself called `Unique<T>` which: | |
84 | ||
85 | * wraps a `*const T` for variance | |
5bcae85e | 86 | * includes a `PhantomData<T>` |
8bb4bdeb XL |
87 | * auto-derives `Send`/`Sync` as if T was contained |
88 | * marks the pointer as `NonZero` for the null-pointer optimization | |
89 | ||
90 | ## Table of `PhantomData` patterns | |
91 | ||
92 | Here’s a table of all the wonderful ways `PhantomData` could be used: | |
93 | ||
94 | | Phantom type | `'a` | `T` | | |
95 | |-----------------------------|-----------|---------------------------| | |
29967ef6 XL |
96 | | `PhantomData<T>` | - | covariant (with drop check) | |
97 | | `PhantomData<&'a T>` | covariant | covariant | | |
98 | | `PhantomData<&'a mut T>` | covariant | invariant | | |
99 | | `PhantomData<*const T>` | - | covariant | | |
8bb4bdeb | 100 | | `PhantomData<*mut T>` | - | invariant | |
dfeec247 | 101 | | `PhantomData<fn(T)>` | - | contravariant | |
29967ef6 | 102 | | `PhantomData<fn() -> T>` | - | covariant | |
8bb4bdeb XL |
103 | | `PhantomData<fn(T) -> T>` | - | invariant | |
104 | | `PhantomData<Cell<&'a ()>>` | invariant | - | |