Skip to content

Commit 94f7a79

Browse files
[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.
1 parent 4d941cd commit 94f7a79

File tree

1 file changed

+87
-0
lines changed

1 file changed

+87
-0
lines changed

Diff for: library/core/src/future/poll_fn.rs

+87
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,93 @@ use crate::task::{Context, Poll};
2424
/// assert_eq!(read_future.await, "Hello, World!".to_owned());
2525
/// # }
2626
/// ```
27+
///
28+
/// ## Capturing a pinned state
29+
///
30+
/// Example of a closure wrapping inner futures:
31+
///
32+
/// ```
33+
/// # async fn run() {
34+
/// use core::future::{self, Future};
35+
/// use core::task::Poll;
36+
///
37+
/// /// Resolves to the first future that completes. In the event of a tie, `a` wins.
38+
/// fn naive_select<T>(
39+
/// a: impl Future<Output = T>,
40+
/// b: impl Future<Output = T>,
41+
/// ) -> impl Future<Output = T>
42+
/// {
43+
/// let (mut a, mut b) = (Box::pin(a), Box::pin(b));
44+
/// future::poll_fn(move |cx| {
45+
/// if let Poll::Ready(r) = a.as_mut().poll(cx) {
46+
/// Poll::Ready(r)
47+
/// } else if let Poll::Ready(r) = b.as_mut().poll(cx) {
48+
/// Poll::Ready(r)
49+
/// } else {
50+
/// Poll::Pending
51+
/// }
52+
/// })
53+
/// }
54+
///
55+
/// let a = async { 42 };
56+
/// let b = future::pending();
57+
/// let v = naive_select(a, b).await;
58+
/// assert_eq!(v, 42);
59+
///
60+
/// let a = future::pending();
61+
/// let b = async { 27 };
62+
/// let v = naive_select(a, b).await;
63+
/// assert_eq!(v, 27);
64+
///
65+
/// let a = async { 42 };
66+
/// let b = async { 27 };
67+
/// let v = naive_select(a, b).await;
68+
/// assert_eq!(v, 42); // biased towards `a` in case of tie!
69+
/// # }
70+
/// ```
71+
///
72+
/// This time without [`Box::pin`]ning:
73+
///
74+
/// [`Box::pin`]: ../../std/boxed/struct.Box.html#method.pin
75+
///
76+
/// ```
77+
/// # async fn run() {
78+
/// use core::future::{self, Future};
79+
/// use core::pin::pin;
80+
/// use core::task::Poll;
81+
///
82+
/// /// Resolves to the first future that completes. In the event of a tie, `a` wins.
83+
/// fn naive_select<T>(
84+
/// a: impl Future<Output = T>,
85+
/// b: impl Future<Output = T>,
86+
/// ) -> impl Future<Output = T>
87+
/// {
88+
/// async {
89+
/// let (mut a, mut b) = (pin!(a), pin!(b));
90+
/// future::poll_fn(move |cx| {
91+
/// if let Poll::Ready(r) = a.as_mut().poll(cx) {
92+
/// Poll::Ready(r)
93+
/// } else if let Poll::Ready(r) = b.as_mut().poll(cx) {
94+
/// Poll::Ready(r)
95+
/// } else {
96+
/// Poll::Pending
97+
/// }
98+
/// }).await
99+
/// }
100+
/// }
101+
///
102+
/// let a = async { 42 };
103+
/// let b = future::pending();
104+
/// let v = naive_select(a, b).await;
105+
/// assert_eq!(v, 42);
106+
/// # }
107+
/// ```
108+
///
109+
/// - Notice how, by virtue of being in an `async` context, we have been able to make the [`pin!`]
110+
/// macro work, thereby avoiding any need for the `unsafe`
111+
/// <code>[Pin::new_unchecked](&mut fut)</code> constructor.
112+
///
113+
/// [`pin!`]: crate::pin::pin!
27114
#[stable(feature = "future_poll_fn", since = "1.64.0")]
28115
pub fn poll_fn<T, F>(f: F) -> PollFn<F>
29116
where

0 commit comments

Comments
 (0)