rewrite pin module intro

This commit is contained in:
Ralf Jung 2019-02-19 20:50:16 +01:00
parent 0ba99f62d1
commit 3040380206
2 changed files with 27 additions and 24 deletions

View file

@ -610,7 +610,8 @@ unsafe impl<T: ?Sized> Freeze for &mut T {}
/// which then allows it to move out with functions such as [`mem::replace`].
///
/// `Unpin` has no consequence at all for non-pinned data. In particular,
/// [`mem::replace`] happily moves `!Unpin` data. However, you cannot use
/// [`mem::replace`] happily moves `!Unpin` data (it works for any `&mut T`, not
/// just when `T: Unpin`). However, you cannot use
/// [`mem::replace`] on data wrapped inside a [`Pin`] because you cannot get the
/// `&mut T` you need for that, and *that* is what makes this system work.
///

View file

@ -2,33 +2,35 @@
//!
//! 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.
//! [`Pin`] ensures that the pointee of any pointer type has a stable location in memory,
//! meaning it cannot be moved elsewhere and its memory cannot be deallocated
//! until it gets dropped. We say that the pointee is "pinned".
//!
//! Since data can be moved out of `&mut` and `Box` with functions such as [`mem::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.
//! By default, all types in Rust are movable. Rust allows passing all types by-value,
//! and common smart-pointer types such as `Box` and `&mut` allow replacing and
//! moving the values they contain: you can move out of a `Box`, or you can use [`mem::swap`].
//! [`Pin`] wraps a pointer type, so `Pin<Box<T>>` functions much like a regular `Box<T>`
//! (when a `Pin<Box<T>>` gets dropped, so do its contents, and the memory gets deallocated).
//! Similarily, `Pin<&mut T>` is a lot like `&mut T`. However, [`Pin`] does not let clients actually
//! obtain a `Box` or reference to pinned data, which implies that you cannot use
//! operations such as [`mem::swap`]:
//! ```
//! fn swap_pins<T>(x: Pin<&mut T>, y: Pin<&mut T>) {
//! // `mem::swap` needs `&mut T`, but we cannot get it.
//! // We are stuck, we cannot swap the contents of these references.
//! // We could use `Pin::get_unchecked_mut`, but that is unsafe for a reason:
//! // we are not allowed to use it for moving things out of the `Pin`.
//! }
//! ```
//!
//! It is worth reiterating that [`Pin`] does *not* change the fact that a Rust compiler
//! considers all types movable. [`mem::swap`] remains callable for any `T`. Instead, `Pin`
//! prevents certain *values* (pointed to by pointers wrapped in `Pin`) from being
//! moved by making it impossible to call methods like [`mem::swap`] on them. These
//! methods all need an `&mut T`, and you cannot obtain that from a `Pin`.
//! moved by making it impossible to call methods like [`mem::swap`] on them.
//!
//! # `Unpin`
//!
@ -43,7 +45,7 @@
//! `Unpin` has no effect on the behavior of `Pin<Box<T>>` (here, `T` is the
//! pointed-to type).
//!
//! # Example: Self-referential struct
//! # Example: self-referential struct
//!
//! ```rust
//! use std::pin::Pin;
@ -119,7 +121,7 @@
//! To make this work, not just moving the data is restricted; deallocating, repurposing or
//! otherwise invalidating the memory used to store the data is restricted, too.
//! Concretely, for pinned data you have to maintain the invariant
//! that *its memory will not get invalidated from the momentit gets pinned until
//! that *its memory will not get invalidated from the moment it gets pinned until
//! when `drop` is called*. Memory can be invalidated by deallocation, but also by
//! replacing a `Some(v)` by `None`, or calling `Vec::set_len` to "kill" some elements
//! off of a vector.
@ -318,13 +320,13 @@ impl<P: Deref> Pin<P> {
/// # Safety
///
/// This constructor is unsafe because we cannot guarantee that the data
/// pointed to by `pointer` is pinned forever. If the constructed `Pin<P>` does
/// pointed to by `pointer` is pinned, meaning that the data will not be moved or
/// its storage invalidated until it gets dropped. If the constructed `Pin<P>` does
/// not guarantee that the data `P` points to is pinned, constructing a
/// `Pin<P>` is unsafe. In particular, calling `Pin::new_unchecked`
/// on an `&'a mut T` is unsafe because while you are able to pin it for the given
/// lifetime `'a`, you have no control over whether it is kept pinned once `'a`
/// ends. A value, once pinned, must remain pinned forever
/// (unless its type implements `Unpin`).
/// ends. A value, once pinned, must remain pinned forever (unless its type implements `Unpin`).
///
/// By using this method, you are making a promise about the `P::Deref` and
/// `P::DerefMut` implementations, if they exist. Most importantly, they