diff options
| author | Ralf Jung <post@ralfj.de> | 2019-02-19 21:12:48 +0100 |
|---|---|---|
| committer | Ralf Jung <post@ralfj.de> | 2019-02-19 21:12:48 +0100 |
| commit | c8f4efc006c24bfb427e9dd70853fb3e41584aa9 (patch) | |
| tree | da654fc2d9cd69cc87b67fe134246f1d5a48b4dc /src/libcore | |
| parent | c774bc650a6b19e6ca6b970dda294ba8937a6548 (diff) | |
| download | rust-c8f4efc006c24bfb427e9dd70853fb3e41584aa9.tar.gz rust-c8f4efc006c24bfb427e9dd70853fb3e41584aa9.zip | |
mention interaction with Deref in intro
Diffstat (limited to 'src/libcore')
| -rw-r--r-- | src/libcore/pin.rs | 47 |
1 files changed, 38 insertions, 9 deletions
diff --git a/src/libcore/pin.rs b/src/libcore/pin.rs index a142fd811ad..8acc215d017 100644 --- a/src/libcore/pin.rs +++ b/src/libcore/pin.rs @@ -19,6 +19,7 @@ //! obtain a `Box` or reference to pinned data, which implies that you cannot use //! operations such as [`mem::swap`]: //! ``` +//! use std::pin::Pin; //! 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. @@ -32,6 +33,15 @@ //! 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. //! +//! [`Pin`] can be used to wrap any pointer type, and as such it interacts with +//! [`Deref`] and [`DerefMut`]. A `Pin<P>` where `P: Deref` should be considered +//! as a "`P`-style pointer" to a pinned `P::Target` -- so, a `Pin<Box<T>>` is +//! an owned pointer to a pinned `T`, and a `Pin<Rc<T>>` is a reference-counted +//! pointer to a pinned `T`. +//! For correctness, [`Pin`] relies on the [`Deref`] and [`DerefMut`] implementations +//! to not move out of their `self` parameter, and to only ever return a pointer +//! to pinned data when they are called on a pinned pointer. +//! //! # `Unpin` //! //! However, these restrictions are usually not necessary. Many types are always freely @@ -114,7 +124,7 @@ //! list element will patch the pointers of its predecessor and successor to remove itself //! from the list. //! -//! To make this work, it is crucial taht we can actually rely on `drop` being called. +//! To make this work, it is crucial that we can actually rely on `drop` being called. //! And, in fact, this is a guarantee that `Pin` provides. //! //! # `Drop` guarantee @@ -219,6 +229,8 @@ //! //! [`Pin`]: struct.Pin.html //! [`Unpin`]: ../../std/marker/trait.Unpin.html +//! [`Deref`]: ../../std/ops/trait.Deref.html +//! [`DerefMut`]: ../../std/ops/trait.DerefMut.html //! [`mem::swap`]: ../../std/mem/fn.swap.html //! [`mem::forget`]: ../../std/mem/fn.forget.html //! [`Box`]: ../../std/boxed/struct.Box.html @@ -319,16 +331,16 @@ impl<P: Deref> Pin<P> { /// Construct a new `Pin` around a reference to some data of a type that /// may or may not implement `Unpin`. /// + /// If `pointer` dereferences to an `Unpin` type, `Pin::new` should be used + /// instead. + /// /// # Safety /// /// This constructor is unsafe because we cannot guarantee that the data /// 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`). + /// `Pin<P>` is unsafe. In particular, /// /// By using this method, you are making a promise about the `P::Deref` and /// `P::DerefMut` implementations, if they exist. Most importantly, they @@ -340,21 +352,38 @@ impl<P: Deref> Pin<P> { /// must not be possible to obtain a `&mut P::Target` and then /// move out of that reference (using, for example [`mem::swap`]). /// - /// For example, the following is a *violation* of `Pin`'s safety: + /// For example, 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: /// ``` /// use std::mem; /// use std::pin::Pin; /// - /// fn foo<T>(mut a: T, mut b: T) { + /// fn move_pinned_ref<T>(mut a: T, mut b: T) { /// unsafe { let p = Pin::new_unchecked(&mut a); } // should mean `a` can never move again /// mem::swap(&mut a, &mut b); /// // the address of `a` changed to `b`'s stack slot, so `a` got moved even /// // though we have previously pinned it! /// } /// ``` + /// A value, once pinned, must remain pinned forever (unless its type implements `Unpin`). /// - /// If `pointer` dereferences to an `Unpin` type, `Pin::new` should be used - /// instead. + /// Similarily, calling `Pin::new_unchecked` on a `Rc<T>` is unsafe because there could be + /// aliases to the same data that are not subject to the pinning restrictions: + /// ``` + /// use std::rc::Rc; + /// use std::pin::Pin; + /// + /// fn move_pinned_rc<T>(mut x: Rc<T>) { + /// let pinned = unsafe { Pin::new_unchecked(x.clone()) }; + /// { let p: Pin<&T> = pinned.as_ref(); } // should mean the pointee can never move again + /// drop(pinned); + /// let content = Rc::get_mut(&mut x).unwrap(); + /// // Now, if `x` was the only reference, we have a mutable reference to + /// // data that we pinned above, which we could use to move it as we have + /// // seen in the previous example. + /// } + /// ``` /// /// [`mem::swap`]: ../../std/mem/fn.swap.html #[stable(feature = "pin", since = "1.33.0")] |
