From 038ce659e346c6fc74c9de1681c6329dac74e19b Mon Sep 17 00:00:00 2001 From: Niv Kaminer Date: Mon, 6 Aug 2018 10:52:35 +0300 Subject: [PATCH 1/7] expand the documentation on the `Unpin` trait provides an overview of the Pin API which the trait is for, and show how it can be used in making self referencial structs part of #49150 --- src/libcore/marker.rs | 94 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 89 insertions(+), 5 deletions(-) diff --git a/src/libcore/marker.rs b/src/libcore/marker.rs index 4f37b462583d1..dec3e46897cce 100644 --- a/src/libcore/marker.rs +++ b/src/libcore/marker.rs @@ -603,15 +603,99 @@ unsafe impl Freeze for *mut T {} unsafe impl<'a, T: ?Sized> Freeze for &'a T {} unsafe impl<'a, T: ?Sized> Freeze for &'a mut T {} -/// Types which can be moved out of a `PinMut`. +/// A trait that indicates that it is safe to move an object of a type implementing it. +/// Since that is true for most types, it is automatically implemented in most cases. +/// This trait is mainly used to build self referencial structs, +/// since moving an object with pointers to itself will invalidate them, +/// causing undefined behavior. /// -/// The `Unpin` trait is used to control the behavior of the [`PinMut`] type. If a -/// type implements `Unpin`, it is safe to move a value of that type out of the -/// `PinMut` pointer. +/// # The Pin API /// -/// This trait is automatically implemented for almost every type. +/// The `Unpin` trait doesn't actually change the behavior of the compiler around moves, +/// so code like this will compile just fine: +/// +/// ```rust +/// #![feature(pin)] +/// use std::marker::Pinned; +/// +/// struct Unmovable { +/// _pin: Pinned, // this marker type prevents Unpin from being implemented for this type +/// } +/// +/// let unmoved = Unmovable { _pin: Pinned }; +/// let moved = unmoved; +/// ``` +/// +/// In order to actually prevent the pinned objects from moving, +/// it has to be wrapped in special pointer types, +/// which currently include [`PinMut`] and [`PinBox`]. +/// +/// The way they work is by implementing [`DerefMut`] for all types that implement Unpin, +/// but only [`Deref`] otherwise. +/// +/// This is done because, while modifying an object can be done in-place, +/// it might also relocate a buffer when its at full capacity, +/// or it might replace one object with another without logically "moving" them with [`swap`]. /// /// [`PinMut`]: ../mem/struct.PinMut.html +/// [`PinBox`]: ../../alloc/boxed/struct.PinMut.html +/// [`DerefMut`]: ../ops/trait.DerefMut.html +/// [`Deref`]: ../ops/trait.Deref.html +/// [`swap`]: ../mem/fn.swap.html +/// +/// # example +/// +/// ```rust +/// #![feature(pin)] +/// +/// use std::boxed::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 moving the data with it that would violate borrowing rules. +/// // instead we use a raw pointer, though one which is known to not be null, +/// // since we know its pointing at the string. +/// struct Unmovable { +/// data: String, +/// slice: NonNull, +/// _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 { +/// 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)); +/// +/// // now the only way to access to data (safely) is immutably, +/// // so this will fail to compile: +/// // still_unmoved.data.push_str(" world"); +/// +/// ``` #[unstable(feature = "pin", issue = "49150")] pub auto trait Unpin {} From 6845dc43cd7381697a363e739ca121c3509dae86 Mon Sep 17 00:00:00 2001 From: Niv Kaminer Date: Mon, 6 Aug 2018 12:02:46 +0300 Subject: [PATCH 2/7] correct explenation on the usage of NonNull --- src/libcore/marker.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libcore/marker.rs b/src/libcore/marker.rs index dec3e46897cce..4214bbb4ac44a 100644 --- a/src/libcore/marker.rs +++ b/src/libcore/marker.rs @@ -654,7 +654,7 @@ unsafe impl<'a, T: ?Sized> Freeze for &'a mut T {} /// /// // 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 moving the data with it that would violate borrowing rules. +/// // 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 its pointing at the string. /// struct Unmovable { From 9b7d71067b2f881fa0cea257b6767a5fe13fbf73 Mon Sep 17 00:00:00 2001 From: Niv Kaminer Date: Mon, 6 Aug 2018 14:01:49 +0300 Subject: [PATCH 3/7] fix link to PinBox --- src/libcore/marker.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libcore/marker.rs b/src/libcore/marker.rs index 4214bbb4ac44a..1f9f319082a99 100644 --- a/src/libcore/marker.rs +++ b/src/libcore/marker.rs @@ -638,7 +638,7 @@ unsafe impl<'a, T: ?Sized> Freeze for &'a mut T {} /// or it might replace one object with another without logically "moving" them with [`swap`]. /// /// [`PinMut`]: ../mem/struct.PinMut.html -/// [`PinBox`]: ../../alloc/boxed/struct.PinMut.html +/// [`PinBox`]: ../../alloc/boxed/struct.PinBox.html /// [`DerefMut`]: ../ops/trait.DerefMut.html /// [`Deref`]: ../ops/trait.Deref.html /// [`swap`]: ../mem/fn.swap.html From 87bbd2eeef1ad321b294e2d3b0fb2dc960390c86 Mon Sep 17 00:00:00 2001 From: Niv Kaminer Date: Mon, 6 Aug 2018 23:52:15 +0300 Subject: [PATCH 4/7] fix style issues in doc comment --- src/libcore/marker.rs | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/libcore/marker.rs b/src/libcore/marker.rs index 1f9f319082a99..d398ca621576b 100644 --- a/src/libcore/marker.rs +++ b/src/libcore/marker.rs @@ -603,8 +603,10 @@ unsafe impl Freeze for *mut T {} unsafe impl<'a, T: ?Sized> Freeze for &'a T {} unsafe impl<'a, T: ?Sized> Freeze for &'a mut T {} -/// A trait that indicates that it is safe to move an object of a type implementing it. -/// Since that is true for most types, it is automatically implemented in most cases. +/// Types that are safe to move. +/// +/// Since moving objects is almost always safe, it is automatically implemented in most cases. +/// /// This trait is mainly used to build self referencial structs, /// since moving an object with pointers to itself will invalidate them, /// causing undefined behavior. @@ -643,7 +645,7 @@ unsafe impl<'a, T: ?Sized> Freeze for &'a mut T {} /// [`Deref`]: ../ops/trait.Deref.html /// [`swap`]: ../mem/fn.swap.html /// -/// # example +/// # Examples /// /// ```rust /// #![feature(pin)] @@ -652,11 +654,11 @@ unsafe impl<'a, T: ?Sized> Freeze for &'a mut T {} /// 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, +/// // 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 its pointing at the string. +/// // 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, @@ -664,9 +666,9 @@ unsafe impl<'a, T: ?Sized> Freeze for &'a mut T {} /// } /// /// impl Unmovable { -/// // to ensure the data doesn't move when the function returns, +/// // 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 +/// // and the only way to access it would be through a pointer to it. /// fn new(data: String) -> PinBox { /// let res = Unmovable { /// data, @@ -685,13 +687,13 @@ unsafe impl<'a, T: ?Sized> Freeze for &'a mut T {} /// } /// /// let unmoved = Unmovable::new("hello".to_string()); -/// // the pointer should point to the correct location, +/// // 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 +/// // Meanwhile, we are free to move the pointer around. /// let mut still_unmoved = unmoved; /// assert_eq!(still_unmoved.slice, NonNull::from(&still_unmoved.data)); /// -/// // now the only way to access to data (safely) is immutably, +/// // Now the only way to access to data (safely) is immutably, /// // so this will fail to compile: /// // still_unmoved.data.push_str(" world"); /// From 68e766afab32a45e55728e11c17b996848a7dd49 Mon Sep 17 00:00:00 2001 From: Niv Kaminer Date: Tue, 7 Aug 2018 21:14:57 +0300 Subject: [PATCH 5/7] remove general pinning information from Unpin trait --- src/libcore/marker.rs | 97 ++++--------------------------------------- 1 file changed, 9 insertions(+), 88 deletions(-) diff --git a/src/libcore/marker.rs b/src/libcore/marker.rs index d398ca621576b..596727a014109 100644 --- a/src/libcore/marker.rs +++ b/src/libcore/marker.rs @@ -603,101 +603,22 @@ unsafe impl Freeze for *mut T {} unsafe impl<'a, T: ?Sized> Freeze for &'a T {} unsafe impl<'a, T: ?Sized> Freeze for &'a mut T {} -/// Types that are safe to move. +/// Types which can be safely moved after being pinned. /// -/// Since moving objects is almost always safe, it is automatically implemented in most cases. +/// Since Rust itself has no notion of immovable types, and will consider moves to always be safe, +/// this trait cannot prevent types from moving by itself. /// -/// This trait is mainly used to build self referencial structs, -/// since moving an object with pointers to itself will invalidate them, -/// causing undefined behavior. +/// Instead it can be used to prevent moves through the type system, +/// by controlling the behavior of special pointers types like [`PinMut`], +/// which "pin" the type in place by wrapping it in a type which can only be dereferenced immutably. /// -/// # The Pin API +/// Implementing this trait lifts the restrictions of pinning off a type, +/// which then allows it to move out of said pointers with functions such as [`swap`]. /// -/// The `Unpin` trait doesn't actually change the behavior of the compiler around moves, -/// so code like this will compile just fine: -/// -/// ```rust -/// #![feature(pin)] -/// use std::marker::Pinned; -/// -/// struct Unmovable { -/// _pin: Pinned, // this marker type prevents Unpin from being implemented for this type -/// } -/// -/// let unmoved = Unmovable { _pin: Pinned }; -/// let moved = unmoved; -/// ``` -/// -/// In order to actually prevent the pinned objects from moving, -/// it has to be wrapped in special pointer types, -/// which currently include [`PinMut`] and [`PinBox`]. -/// -/// The way they work is by implementing [`DerefMut`] for all types that implement Unpin, -/// but only [`Deref`] otherwise. -/// -/// This is done because, while modifying an object can be done in-place, -/// it might also relocate a buffer when its at full capacity, -/// or it might replace one object with another without logically "moving" them with [`swap`]. +/// This trait is automatically implemented for almost every type. /// /// [`PinMut`]: ../mem/struct.PinMut.html -/// [`PinBox`]: ../../alloc/boxed/struct.PinBox.html -/// [`DerefMut`]: ../ops/trait.DerefMut.html -/// [`Deref`]: ../ops/trait.Deref.html /// [`swap`]: ../mem/fn.swap.html -/// -/// # Examples -/// -/// ```rust -/// #![feature(pin)] -/// -/// use std::boxed::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, -/// _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 { -/// 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)); -/// -/// // Now the only way to access to data (safely) is immutably, -/// // so this will fail to compile: -/// // still_unmoved.data.push_str(" world"); -/// -/// ``` #[unstable(feature = "pin", issue = "49150")] pub auto trait Unpin {} From 03530fa5e6a0b039e1a5f96dc164968a36017b6b Mon Sep 17 00:00:00 2001 From: Niv Kaminer Date: Thu, 16 Aug 2018 13:56:08 +0300 Subject: [PATCH 6/7] add example for moving out of pointer --- src/libcore/marker.rs | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/libcore/marker.rs b/src/libcore/marker.rs index 596727a014109..a8e5b1e7584d7 100644 --- a/src/libcore/marker.rs +++ b/src/libcore/marker.rs @@ -609,16 +609,27 @@ unsafe impl<'a, T: ?Sized> Freeze for &'a mut T {} /// this trait cannot prevent types from moving by itself. /// /// Instead it can be used to prevent moves through the type system, -/// by controlling the behavior of special pointers types like [`PinMut`], -/// which "pin" the type in place by wrapping it in a type which can only be dereferenced immutably. +/// by controlling the behavior of special pointer types like [`PinMut`], +/// which "pin" the type in place by not allowing it to be moved out via mutable references. /// /// Implementing this trait lifts the restrictions of pinning off a type, -/// which then allows it to move out of said pointers with functions such as [`swap`]. +/// which then allows it to move out of said pointers, with functions such as [`replace`]. +/// +/// So this, for example, can only be done on types implementing `Unpin`: +/// +/// ```rust +/// #![feature(pin)] +/// use std::mem::{PinMut, replace}; +/// +/// let mut string = "this".to_string(); +/// let mut pinned_string = PinMut::new(&mut string); +/// replace(&mut *pinned_string, "other".to_string()); +/// ``` /// /// This trait is automatically implemented for almost every type. /// /// [`PinMut`]: ../mem/struct.PinMut.html -/// [`swap`]: ../mem/fn.swap.html +/// [`replace`]: ../mem/fn.replace.html #[unstable(feature = "pin", issue = "49150")] pub auto trait Unpin {} From 6ae915b29e6df75c6cb20f2af85497460330f1c4 Mon Sep 17 00:00:00 2001 From: Niv Kaminer Date: Fri, 17 Aug 2018 22:28:05 +0300 Subject: [PATCH 7/7] clarify use of Unpin and pinning types --- src/libcore/marker.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/libcore/marker.rs b/src/libcore/marker.rs index a8e5b1e7584d7..83a50e6695e83 100644 --- a/src/libcore/marker.rs +++ b/src/libcore/marker.rs @@ -610,10 +610,10 @@ unsafe impl<'a, T: ?Sized> Freeze for &'a mut T {} /// /// Instead it can be used to prevent moves through the type system, /// by controlling the behavior of special pointer types like [`PinMut`], -/// which "pin" the type in place by not allowing it to be moved out via mutable references. +/// which "pin" the type in place by not allowing it to be moved out of them. /// /// Implementing this trait lifts the restrictions of pinning off a type, -/// which then allows it to move out of said pointers, with functions such as [`replace`]. +/// which then allows it to move out with functions such as [`replace`]. /// /// So this, for example, can only be done on types implementing `Unpin`: /// @@ -623,6 +623,8 @@ unsafe impl<'a, T: ?Sized> Freeze for &'a mut T {} /// /// let mut string = "this".to_string(); /// let mut pinned_string = PinMut::new(&mut string); +/// +/// // dereferencing the pointer mutably is only possible because String implements Unpin /// replace(&mut *pinned_string, "other".to_string()); /// ``` ///