1.33.0[−][src]Module boolean_enums::lstd::pin
Types which pin data to its location in memory
It is sometimes useful to have objects that are guaranteed to not move, in the sense that their placement in memory does not change, and can thus be relied upon.
A prime example of such a scenario would be building self-referential structs, since moving an object with pointers to itself will invalidate them, which could cause undefined behavior.
By default, all types in Rust are movable. Rust allows passing all types by-value,
and common smart-pointer types such as Box
, Rc
, and &mut
allow replacing and
moving the values they contain. In order to prevent objects from moving, they must
be pinned by wrapping a pointer to the data in the Pin
type.
Doing this prohibits moving the value behind the pointer.
For example, Pin<Box<T>>
functions much like a regular Box<T>
,
but doesn't allow moving T
. The pointer value itself (the Box
) can still be moved,
but the value behind it cannot.
Since data can be moved out of &mut
and Box
with functions such as swap
,
changing the location of the underlying data, Pin
prohibits accessing the
underlying pointer type (the &mut
or Box
) directly, and provides its own set of
APIs for accessing and using the value. Pin
also guarantees that no other
functions will move the pointed-to value. This allows for the creation of
self-references and other special behaviors that are only possible for unmovable
values.
However, these restrictions are usually not necessary. Many types are always freely
movable. These types implement the Unpin
auto-trait, which nullifies the effect
of Pin
. For T: Unpin
, Pin<Box<T>>
and Box<T>
function identically, as do
Pin<&mut T>
and &mut T
.
Note that pinning and Unpin
only affect the pointed-to type. For example, whether
or not Box<T>
is Unpin
has no affect on the behavior of Pin<Box<T>>
. Similarly,
Pin<Box<T>>
and Pin<&mut T>
are always Unpin
themselves, even though the
T
underneath them isn't, because the pointers in Pin<Box<_>>
and Pin<&mut _>
are always freely movable, even if the data they point to isn't.
Examples
use std::pin::Pin; use std::marker::PhantomPinned; use std::ptr::NonNull; // This is a self-referential struct since the slice field points to the data field. // We cannot inform the compiler about that with a normal reference, // since this pattern cannot be described with the usual borrowing rules. // Instead we use a raw pointer, though one which is known to not be null, // since we know it's pointing at the string. struct Unmovable { data: String, slice: NonNull<String>, _pin: PhantomPinned, } impl Unmovable { // To ensure the data doesn't move when the function returns, // we place it in the heap where it will stay for the lifetime of the object, // and the only way to access it would be through a pointer to it. fn new(data: String) -> Pin<Box<Self>> { let res = Unmovable { data, // we only create the pointer once the data is in place // otherwise it will have already moved before we even started slice: NonNull::dangling(), _pin: PhantomPinned, }; let mut boxed = Box::pin(res); let slice = NonNull::from(&boxed.data); // we know this is safe because modifying a field doesn't move the whole struct unsafe { let mut_ref: Pin<&mut Self> = Pin::as_mut(&mut boxed); Pin::get_unchecked_mut(mut_ref).slice = slice; } boxed } } let unmoved = Unmovable::new("hello".to_string()); // The pointer should point to the correct location, // so long as the struct hasn't moved. // Meanwhile, we are free to move the pointer around. let mut still_unmoved = unmoved; assert_eq!(still_unmoved.slice, NonNull::from(&still_unmoved.data)); // Since our type doesn't implement Unpin, this will fail to compile: // let new_unmoved = Unmovable::new("world".to_string()); // std::mem::swap(&mut *still_unmoved, &mut *new_unmoved);
Structs
Pin | A pinned pointer. |