You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
nomicon/src/phantom-data.md

84 lines
2.9 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# PhantomData
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:
```rust,ignore
struct Iter<'a, T: 'a> {
ptr: *const T,
end: *const T,
}
```
However because `'a` is unused within the struct's body, it's *unbounded*.
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 being useful
for secondary concerns like deriving Send and Sync.
When using the *drop check eyepatch*, PhantomData also becomes important for
telling the compiler about all types that you drop that it can't see. See the
[the previous section][dropck-eyepatch] for details. This can be ignored if you
don't know what the eyepatch is.
Iter logically contains a bunch of `&'a T`s, so this is exactly what we tell
the PhantomData to simulate:
```
use std::marker;
struct Iter<'a, T: 'a> {
ptr: *const T,
end: *const T,
_marker: marker::PhantomData<&'a T>,
}
```
and that's it. The lifetime will be bounded, and your iterator will be variant
over `'a` and `T`. Everything Just Works.
Here's a more extreme example based on HashMap which stores a single opaque
allocation which is used for multiple arrays of different types:
```
use std::marker;
struct HashMap<K, V> {
ptr: *mut u8,
// The pointer actually stores keys and values
// (and hashes, but those aren't generic)
_marker: marker::PhantomData<(K, V)>,
}
```
## Table of `PhantomData` patterns
Heres a table of all the most common ways `PhantomData` is used:
| Phantom type | `'a` | `T` |
|-----------------------------|-----------|---------------------------|
| `PhantomData<T>` | - | variant (and drop check T)|
| `PhantomData<&'a T>` | variant | variant |
| `PhantomData<&'a mut T>` | variant | invariant |
| `PhantomData<*const T>` | - | variant |
| `PhantomData<*mut T>` | - | invariant |
| `PhantomData<fn(T)>` | - | contravariant |
| `PhantomData<fn() -> T>` | - | variant |
| `PhantomData<fn(T) -> T>` | - | invariant |
| `PhantomData<Cell<&'a ()>>` | invariant | - |
[dropck-eyepatch]: dropck-eyepatch.html