@@ -543,7 +543,7 @@ impl<P: Deref> Pin<P> {
543
543
/// let p: Pin<&mut T> = Pin::new_unchecked(&mut a);
544
544
/// // This should mean the pointee `a` can never move again.
545
545
/// }
546
- /// mem::swap(&mut a, &mut b);
546
+ /// mem::swap(&mut a, &mut b); // Potential UB down the road ⚠️
547
547
/// // The address of `a` changed to `b`'s stack slot, so `a` got moved even
548
548
/// // though we have previously pinned it! We have violated the pinning API contract.
549
549
/// }
@@ -563,13 +563,66 @@ impl<P: Deref> Pin<P> {
563
563
/// // This should mean the pointee can never move again.
564
564
/// }
565
565
/// drop(pinned);
566
- /// let content = Rc::get_mut(&mut x).unwrap();
566
+ /// let content = Rc::get_mut(&mut x).unwrap(); // Potential UB down the road ⚠️
567
567
/// // Now, if `x` was the only reference, we have a mutable reference to
568
568
/// // data that we pinned above, which we could use to move it as we have
569
569
/// // seen in the previous example. We have violated the pinning API contract.
570
570
/// }
571
571
/// ```
572
572
///
573
+ /// ## Pinning of closure captures
574
+ ///
575
+ /// Particular care is required when using `Pin::new_unchecked` in a closure:
576
+ /// `Pin::new_unchecked(&mut var)` where `var` is a by-value (moved) closure capture
577
+ /// implicitly makes the promise that the closure itself is pinned, and that *all* uses
578
+ /// of this closure capture respect that pinning.
579
+ /// ```
580
+ /// use std::pin::Pin;
581
+ /// use std::task::Context;
582
+ /// use std::future::Future;
583
+ ///
584
+ /// fn move_pinned_closure(mut x: impl Future, cx: &mut Context<'_>) {
585
+ /// // Create a closure that moves `x`, and then internally uses it in a pinned way.
586
+ /// let mut closure = move || unsafe {
587
+ /// let _ignore = Pin::new_unchecked(&mut x).poll(cx);
588
+ /// };
589
+ /// // Call the closure, so the future can assume it has been pinned.
590
+ /// closure();
591
+ /// // Move the closure somewhere else. This also moves `x`!
592
+ /// let mut moved = closure;
593
+ /// // Calling it again means we polled the future from two different locations,
594
+ /// // violating the pinning API contract.
595
+ /// moved(); // Potential UB ⚠️
596
+ /// }
597
+ /// ```
598
+ /// When passing a closure to another API, it might be moving the closure any time, so
599
+ /// `Pin::new_unchecked` on closure captures may only be used if the API explicitly documents
600
+ /// that the closure is pinned.
601
+ ///
602
+ /// The better alternative is to avoid all that trouble and do the pinning in the outer function
603
+ /// instead (here using the unstable `pin` macro):
604
+ /// ```
605
+ /// #![feature(pin_macro)]
606
+ /// use std::pin::pin;
607
+ /// use std::task::Context;
608
+ /// use std::future::Future;
609
+ ///
610
+ /// fn move_pinned_closure(mut x: impl Future, cx: &mut Context<'_>) {
611
+ /// let mut x = pin!(x);
612
+ /// // Create a closure that captures `x: Pin<&mut _>`, which is safe to move.
613
+ /// let mut closure = move || {
614
+ /// let _ignore = x.as_mut().poll(cx);
615
+ /// };
616
+ /// // Call the closure, so the future can assume it has been pinned.
617
+ /// closure();
618
+ /// // Move the closure somewhere else.
619
+ /// let mut moved = closure;
620
+ /// // Calling it again here is fine (except that we might be polling a future that already
621
+ /// // returned `Poll::Ready`, but that is a separate problem).
622
+ /// moved();
623
+ /// }
624
+ /// ```
625
+ ///
573
626
/// [`mem::swap`]: crate::mem::swap
574
627
#[ lang = "new_unchecked" ]
575
628
#[ inline( always) ]
0 commit comments