Skip to content

Commit 038ce65

Browse files
committed
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
1 parent aa1e6db commit 038ce65

File tree

1 file changed

+89
-5
lines changed

1 file changed

+89
-5
lines changed

src/libcore/marker.rs

+89-5
Original file line numberDiff line numberDiff line change
@@ -603,15 +603,99 @@ unsafe impl<T: ?Sized> Freeze for *mut T {}
603603
unsafe impl<'a, T: ?Sized> Freeze for &'a T {}
604604
unsafe impl<'a, T: ?Sized> Freeze for &'a mut T {}
605605

606-
/// Types which can be moved out of a `PinMut`.
606+
/// A trait that indicates that it is safe to move an object of a type implementing it.
607+
/// Since that is true for most types, it is automatically implemented in most cases.
608+
/// This trait is mainly used to build self referencial structs,
609+
/// since moving an object with pointers to itself will invalidate them,
610+
/// causing undefined behavior.
607611
///
608-
/// The `Unpin` trait is used to control the behavior of the [`PinMut`] type. If a
609-
/// type implements `Unpin`, it is safe to move a value of that type out of the
610-
/// `PinMut` pointer.
612+
/// # The Pin API
611613
///
612-
/// This trait is automatically implemented for almost every type.
614+
/// The `Unpin` trait doesn't actually change the behavior of the compiler around moves,
615+
/// so code like this will compile just fine:
616+
///
617+
/// ```rust
618+
/// #![feature(pin)]
619+
/// use std::marker::Pinned;
620+
///
621+
/// struct Unmovable {
622+
/// _pin: Pinned, // this marker type prevents Unpin from being implemented for this type
623+
/// }
624+
///
625+
/// let unmoved = Unmovable { _pin: Pinned };
626+
/// let moved = unmoved;
627+
/// ```
628+
///
629+
/// In order to actually prevent the pinned objects from moving,
630+
/// it has to be wrapped in special pointer types,
631+
/// which currently include [`PinMut`] and [`PinBox`].
632+
///
633+
/// The way they work is by implementing [`DerefMut`] for all types that implement Unpin,
634+
/// but only [`Deref`] otherwise.
635+
///
636+
/// This is done because, while modifying an object can be done in-place,
637+
/// it might also relocate a buffer when its at full capacity,
638+
/// or it might replace one object with another without logically "moving" them with [`swap`].
613639
///
614640
/// [`PinMut`]: ../mem/struct.PinMut.html
641+
/// [`PinBox`]: ../../alloc/boxed/struct.PinMut.html
642+
/// [`DerefMut`]: ../ops/trait.DerefMut.html
643+
/// [`Deref`]: ../ops/trait.Deref.html
644+
/// [`swap`]: ../mem/fn.swap.html
645+
///
646+
/// # example
647+
///
648+
/// ```rust
649+
/// #![feature(pin)]
650+
///
651+
/// use std::boxed::PinBox;
652+
/// use std::marker::Pinned;
653+
/// use std::ptr::NonNull;
654+
///
655+
/// // this is a self referencial struct since the slice field points to the data field.
656+
/// // we cannot inform the compiler about that with a normal reference,
657+
/// // since moving the data with it that would violate borrowing rules.
658+
/// // instead we use a raw pointer, though one which is known to not be null,
659+
/// // since we know its pointing at the string.
660+
/// struct Unmovable {
661+
/// data: String,
662+
/// slice: NonNull<String>,
663+
/// _pin: Pinned,
664+
/// }
665+
///
666+
/// impl Unmovable {
667+
/// // to ensure the data doesn't move when the function returns,
668+
/// // we place it in the heap where it will stay for the lifetime of the object,
669+
/// // and the only way to access it would be through a pointer to it
670+
/// fn new(data: String) -> PinBox<Self> {
671+
/// let res = Unmovable {
672+
/// data,
673+
/// // we only create the pointer once the data is in place
674+
/// // otherwise it will have already moved before we even started
675+
/// slice: NonNull::dangling(),
676+
/// _pin: Pinned,
677+
/// };
678+
/// let mut boxed = PinBox::new(res);
679+
///
680+
/// let slice = NonNull::from(&boxed.data);
681+
/// // we know this is safe because modifying a field doesn't move the whole struct
682+
/// unsafe { PinBox::get_mut(&mut boxed).slice = slice };
683+
/// boxed
684+
/// }
685+
/// }
686+
///
687+
/// let unmoved = Unmovable::new("hello".to_string());
688+
/// // the pointer should point to the correct location,
689+
/// // so long as the struct hasn't moved.
690+
/// // meanwhile, we are free to move the pointer around
691+
/// let mut still_unmoved = unmoved;
692+
/// assert_eq!(still_unmoved.slice, NonNull::from(&still_unmoved.data));
693+
///
694+
/// // now the only way to access to data (safely) is immutably,
695+
/// // so this will fail to compile:
696+
/// // still_unmoved.data.push_str(" world");
697+
///
698+
/// ```
615699
#[unstable(feature = "pin", issue = "49150")]
616700
pub auto trait Unpin {}
617701

0 commit comments

Comments
 (0)