diff --git a/futures-util/src/future/mod.rs b/futures-util/src/future/mod.rs index 2209ffd56..6e599f612 100644 --- a/futures-util/src/future/mod.rs +++ b/futures-util/src/future/mod.rs @@ -65,6 +65,9 @@ pub use self::try_maybe_done::{try_maybe_done, TryMaybeDone}; mod option; pub use self::option::OptionFuture; +mod or_pending; +pub use self::or_pending::OrPending; + mod poll_fn; pub use self::poll_fn::{poll_fn, PollFn}; diff --git a/futures-util/src/future/or_pending.rs b/futures-util/src/future/or_pending.rs new file mode 100644 index 000000000..f99f993a6 --- /dev/null +++ b/futures-util/src/future/or_pending.rs @@ -0,0 +1,88 @@ +//! Definition of [`OrPending`] future wrapper. + +use core::pin::Pin; +use futures_core::future::{FusedFuture, Future}; +use futures_core::task::{Context, Poll}; +use pin_project_lite::pin_project; + +pin_project! { + /// A wrapper for a [`Future`] which may or may not be present. + /// + /// If the inner future is present, this wrapper will resolve when it does. + /// Otherwise this wrapper will never resolve (`Future::poll` will always + /// return [`Poll::Pending`]). Created by the [`From`] implementation for + /// [`Option`](std::option::Option). + /// + /// # Examples + /// + /// ``` + /// # futures::executor::block_on(async { + /// use futures::future::OptionFuture; + /// + /// let mut a: OptionFuture<_> = Some(async { 123 }).into(); + /// assert_eq!(a.await, Some(123)); + /// + /// a = None.into(); + /// assert_eq!(a.await, None); + /// # }); + /// ``` + #[derive(Debug, Clone)] + #[must_use = "futures do nothing unless you `.await` or poll them"] + pub struct OrPending { + #[pin] + inner: Option, + } +} + +impl OrPending { + /// Gets a mutable reference to the inner future (if any). + pub fn as_pin_mut(self: Pin<&mut Self>) -> Option> { + self.project().inner.as_pin_mut() + } + + /// Drops the inner future. + /// + /// After this, all calls to [`Future::poll`] will return [`Poll::Pending`]. + pub fn reset(self: Pin<&mut Self>) { + self.project().inner.set(None); + } +} + +impl Default for OrPending { + fn default() -> Self { + Self { inner: None } + } +} + +impl Future for OrPending { + type Output = F::Output; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let mut this = self.project(); + match this.inner.as_mut().as_pin_mut() { + None => Poll::Pending, + Some(x) => match x.poll(cx) { + Poll::Pending => Poll::Pending, + Poll::Ready(t) => { + this.inner.set(None); + Poll::Ready(t) + } + }, + } + } +} + +impl FusedFuture for OrPending { + fn is_terminated(&self) -> bool { + match &self.inner { + Some(x) => x.is_terminated(), + None => true, + } + } +} + +impl From> for OrPending { + fn from(option: Option) -> Self { + Self { inner: option } + } +}