W3cubDocs

/Rust

Module std::pin

🔬 This is a nightly-only experimental API. (pin #49150)

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-referencial structs, since moving an object with pointers to itself will invalidate them, which could cause undefined behavior.

In order to prevent objects from moving, they must be pinned, by wrapping the data in pinning pointer types, such as PinMut and PinBox, which are otherwise equivalent to & mut and Box, respectively.

First of all, these are pointer types because pinned data mustn't be passed around by value (that would change its location in memory). Secondly, since data can be moved out of &mut and Box with functions such as swap, which causes their contents to swap places in memory, we need dedicated types that prohibit such operations.

However, these restrictions are usually not necessary, so most types implement the Unpin auto-trait, which indicates that the type can be moved out safely. Doing so removes the limitations of pinning types, making them the same as their non-pinning counterparts.

Examples

#![feature(pin)]

use std::pin::PinBox;
use std::marker::Pinned;
use std::ptr::NonNull;

// This is a self referencial 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: Pinned,
}

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) -> PinBox<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: Pinned,
        };
        let mut boxed = PinBox::new(res);

        let slice = NonNull::from(&boxed.data);
        // we know this is safe because modifying a field doesn't move the whole struct
        unsafe { PinBox::get_mut(&mut boxed).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

PinBox [
Experimental
]

A pinned, heap allocated reference.

PinMut [
Experimental
]

A pinned reference.

Traits

Unpin [
Experimental
]

Types which can be safely moved after being pinned.

© 2010 The Rust Project Developers
Licensed under the Apache License, Version 2.0 or the MIT license, at your option.
https://doc.rust-lang.org/std/pin/index.html