about summary refs log tree commit diff
diff options
context:
space:
mode:
authorDaniel Henry-Mantilla <daniel.henry.mantilla@gmail.com>2023-04-05 13:06:49 +0200
committerDaniel Henry-Mantilla <daniel.henry.mantilla@gmail.com>2023-06-18 09:56:13 +0000
commit94f7a7931c035473565c1b2ec0a6c2ffaa3b4f79 (patch)
treee1cba704cf0d9b828960ab13a21e1bc2a0d0b01d
parent4d941cd9812891af3b83dd4de64aa7d8ee99641a (diff)
downloadrust-94f7a7931c035473565c1b2ec0a6c2ffaa3b4f79.tar.gz
rust-94f7a7931c035473565c1b2ec0a6c2ffaa3b4f79.zip
[doc] poll_fn: explain how to pin captured state safely
Usage of `Pin::new_unchecked(&mut …)` is dangerous with `poll_fn`, even
though the `!Unpin`-infectiousness has made things smoother.
Nonetheless, there are easy ways to avoid the need for any `unsafe`
altogether, be it through `Box::pin`ning, or the `pin!` macro. Since the
latter only works within an `async` context, showing an example
artifically introducing one ought to help people navigate this subtlety
with safety and confidence.
-rw-r--r--library/core/src/future/poll_fn.rs87
1 files changed, 87 insertions, 0 deletions
diff --git a/library/core/src/future/poll_fn.rs b/library/core/src/future/poll_fn.rs
index 90cb797391a..d27a9dfc176 100644
--- a/library/core/src/future/poll_fn.rs
+++ b/library/core/src/future/poll_fn.rs
@@ -24,6 +24,93 @@ use crate::task::{Context, Poll};
 /// assert_eq!(read_future.await, "Hello, World!".to_owned());
 /// # }
 /// ```
+///
+/// ## Capturing a pinned state
+///
+/// Example of a closure wrapping inner futures:
+///
+/// ```
+/// # async fn run() {
+/// use core::future::{self, Future};
+/// use core::task::Poll;
+///
+/// /// Resolves to the first future that completes. In the event of a tie, `a` wins.
+/// fn naive_select<T>(
+///     a: impl Future<Output = T>,
+///     b: impl Future<Output = T>,
+/// ) -> impl Future<Output = T>
+/// {
+///     let (mut a, mut b) = (Box::pin(a), Box::pin(b));
+///     future::poll_fn(move |cx| {
+///         if let Poll::Ready(r) = a.as_mut().poll(cx) {
+///             Poll::Ready(r)
+///         } else if let Poll::Ready(r) = b.as_mut().poll(cx) {
+///             Poll::Ready(r)
+///         } else {
+///             Poll::Pending
+///         }
+///     })
+/// }
+///
+/// let a = async { 42 };
+/// let b = future::pending();
+/// let v = naive_select(a, b).await;
+/// assert_eq!(v, 42);
+///
+/// let a = future::pending();
+/// let b = async { 27 };
+/// let v = naive_select(a, b).await;
+/// assert_eq!(v, 27);
+///
+/// let a = async { 42 };
+/// let b = async { 27 };
+/// let v = naive_select(a, b).await;
+/// assert_eq!(v, 42); // biased towards `a` in case of tie!
+/// # }
+/// ```
+///
+/// This time without [`Box::pin`]ning:
+///
+/// [`Box::pin`]: ../../std/boxed/struct.Box.html#method.pin
+///
+/// ```
+/// # async fn run() {
+/// use core::future::{self, Future};
+/// use core::pin::pin;
+/// use core::task::Poll;
+///
+/// /// Resolves to the first future that completes. In the event of a tie, `a` wins.
+/// fn naive_select<T>(
+///     a: impl Future<Output = T>,
+///     b: impl Future<Output = T>,
+/// ) -> impl Future<Output = T>
+/// {
+///     async {
+///         let (mut a, mut b) = (pin!(a), pin!(b));
+///         future::poll_fn(move |cx| {
+///             if let Poll::Ready(r) = a.as_mut().poll(cx) {
+///                 Poll::Ready(r)
+///             } else if let Poll::Ready(r) = b.as_mut().poll(cx) {
+///                 Poll::Ready(r)
+///             } else {
+///                 Poll::Pending
+///             }
+///         }).await
+///     }
+/// }
+///
+/// let a = async { 42 };
+/// let b = future::pending();
+/// let v = naive_select(a, b).await;
+/// assert_eq!(v, 42);
+/// # }
+/// ```
+///
+///   - Notice how, by virtue of being in an `async` context, we have been able to make the [`pin!`]
+///     macro work, thereby avoiding any need for the `unsafe`
+///     <code>[Pin::new_unchecked](&mut fut)</code> constructor.
+///
+/// [`pin!`]: crate::pin::pin!
 #[stable(feature = "future_poll_fn", since = "1.64.0")]
 pub fn poll_fn<T, F>(f: F) -> PollFn<F>
 where