1 //! Definition of the MaybeDone combinator
2
3 use core::mem;
4 use core::pin::Pin;
5 use futures_core::future::{FusedFuture, Future};
6 use futures_core::task::{Context, Poll};
7 use pin_project::{pin_project, project};
8
9 /// A future that may have completed.
10 ///
11 /// This is created by the [`maybe_done()`] function.
12 #[pin_project]
13 #[derive(Debug)]
14 pub enum MaybeDone<Fut: Future> {
15 /// A not-yet-completed future
16 Future(#[pin] Fut),
17 /// The output of the completed future
18 Done(Fut::Output),
19 /// The empty variant after the result of a [`MaybeDone`] has been
20 /// taken using the [`take_output`](MaybeDone::take_output) method.
21 Gone,
22 }
23
24 /// Wraps a future into a `MaybeDone`
25 ///
26 /// # Examples
27 ///
28 /// ```
29 /// # futures::executor::block_on(async {
30 /// use futures::future;
31 /// use futures::pin_mut;
32 ///
33 /// let future = future::maybe_done(async { 5 });
34 /// pin_mut!(future);
35 /// assert_eq!(future.as_mut().take_output(), None);
36 /// let () = future.as_mut().await;
37 /// assert_eq!(future.as_mut().take_output(), Some(5));
38 /// assert_eq!(future.as_mut().take_output(), None);
39 /// # });
40 /// ```
maybe_done<Fut: Future>(future: Fut) -> MaybeDone<Fut>41 pub fn maybe_done<Fut: Future>(future: Fut) -> MaybeDone<Fut> {
42 MaybeDone::Future(future)
43 }
44
45 impl<Fut: Future> MaybeDone<Fut> {
46 /// Returns an [`Option`] containing a mutable reference to the output of the future.
47 /// The output of this method will be [`Some`] if and only if the inner
48 /// future has been completed and [`take_output`](MaybeDone::take_output)
49 /// has not yet been called.
50 #[project]
51 #[inline]
output_mut(self: Pin<&mut Self>) -> Option<&mut Fut::Output>52 pub fn output_mut(self: Pin<&mut Self>) -> Option<&mut Fut::Output> {
53 #[project]
54 match self.project() {
55 MaybeDone::Done(res) => Some(res),
56 _ => None,
57 }
58 }
59
60 /// Attempt to take the output of a `MaybeDone` without driving it
61 /// towards completion.
62 #[inline]
take_output(self: Pin<&mut Self>) -> Option<Fut::Output>63 pub fn take_output(self: Pin<&mut Self>) -> Option<Fut::Output> {
64 // Safety: we return immediately unless we are in the `Done`
65 // state, which does not have any pinning guarantees to uphold.
66 //
67 // Hopefully `pin_project` will support this safely soon:
68 // https://github.com/taiki-e/pin-project/issues/184
69 unsafe {
70 let this = self.get_unchecked_mut();
71 match this {
72 MaybeDone::Done(_) => {},
73 MaybeDone::Future(_) | MaybeDone::Gone => return None,
74 };
75 if let MaybeDone::Done(output) = mem::replace(this, MaybeDone::Gone) {
76 Some(output)
77 } else {
78 unreachable!()
79 }
80 }
81 }
82 }
83
84 impl<Fut: Future> FusedFuture for MaybeDone<Fut> {
is_terminated(&self) -> bool85 fn is_terminated(&self) -> bool {
86 match self {
87 MaybeDone::Future(_) => false,
88 MaybeDone::Done(_) | MaybeDone::Gone => true,
89 }
90 }
91 }
92
93 impl<Fut: Future> Future for MaybeDone<Fut> {
94 type Output = ();
95
96 #[project]
poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>97 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
98 #[project]
99 match self.as_mut().project() {
100 MaybeDone::Future(f) => {
101 let res = ready!(f.poll(cx));
102 self.set(MaybeDone::Done(res));
103 },
104 MaybeDone::Done(_) => {},
105 MaybeDone::Gone => panic!("MaybeDone polled after value taken"),
106 }
107 Poll::Ready(())
108 }
109 }
110