From c8f4efc006c24bfb427e9dd70853fb3e41584aa9 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 19 Feb 2019 21:12:48 +0100 Subject: [PATCH] mention interaction with Deref in intro --- src/libcore/pin.rs | 47 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 38 insertions(+), 9 deletions(-) diff --git a/src/libcore/pin.rs b/src/libcore/pin.rs index a142fd811ad8..8acc215d0173 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(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

` where `P: Deref` should be considered +//! as a "`P`-style pointer" to a pinned `P::Target` -- so, a `Pin>` is +//! an owned pointer to a pinned `T`, and a `Pin>` 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 Pin

{ /// 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

` does /// not guarantee that the data `P` points to is pinned, constructing a - /// `Pin

` 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

` 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 Pin

{ /// 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(mut a: T, mut b: T) { + /// fn move_pinned_ref(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` 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(mut x: Rc) { + /// 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")]