From 6142ea86b2021956263dfd87b2a84683d2a9a3b8 Mon Sep 17 00:00:00 2001 From: Jan Tojnar Date: Thu, 18 Jul 2024 01:20:26 +0200 Subject: [PATCH] Add OptionalFuture helper Unlike the `OptionFuture`, this one will not resolve when made from `None`. --- futures-util/src/future/mod.rs | 3 ++ futures-util/src/future/option.rs | 3 ++ futures-util/src/future/optional.rs | 82 +++++++++++++++++++++++++++++ 3 files changed, 88 insertions(+) create mode 100644 futures-util/src/future/optional.rs diff --git a/futures-util/src/future/mod.rs b/futures-util/src/future/mod.rs index 1280ce9864..558a3ffb86 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 optional; +pub use self::optional::OptionalFuture; + mod poll_fn; pub use self::poll_fn::{poll_fn, PollFn}; diff --git a/futures-util/src/future/option.rs b/futures-util/src/future/option.rs index 0bc377758a..c7321e2adc 100644 --- a/futures-util/src/future/option.rs +++ b/futures-util/src/future/option.rs @@ -10,6 +10,9 @@ pin_project! { /// /// Created by the [`From`] implementation for [`Option`](std::option::Option). /// + /// A future made from `None` will resolve with `None`. + /// If you wish it never resolved, use [`OptionFuture`] instead. + /// /// # Examples /// /// ``` diff --git a/futures-util/src/future/optional.rs b/futures-util/src/future/optional.rs new file mode 100644 index 0000000000..a3f5a38ee7 --- /dev/null +++ b/futures-util/src/future/optional.rs @@ -0,0 +1,82 @@ +//! Definition of the `Optional` (optional step) combinator + +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 future representing an operation which may or may not exist. + /// + /// Created by the [`From`] implementation for [`Option`](std::option::Option). + /// + /// A future made from `None` will never resolve. If you wish + /// it resolved with `None`, use [`OptionFuture`] instead. + /// + /// # Examples + /// + /// ``` + /// # futures::executor::block_on(async { + /// use futures::future::{OptionalFuture, ready}; + /// use futures::select_biased; + /// + /// let mut a: OptionalFuture<_> = Some(ready(123)).into(); + /// let mut b = ready(()); + /// assert_eq!( + /// select_biased! { + /// _ = a => 1, + /// _ = b => 2, + /// }, + /// 1 + /// ); + /// + /// a = None.into(); + /// b = ready(()); + /// assert_eq!( + /// select_biased! { + /// _ = a => 1, + /// _ = b => 2, + /// }, + /// 2 + /// ); + /// # }); + /// ``` + #[derive(Debug, Clone)] + #[must_use = "futures do nothing unless you `.await` or poll them"] + pub struct OptionalFuture { + #[pin] + inner: Option, + } +} + +impl Default for OptionalFuture { + fn default() -> Self { + Self { inner: None } + } +} + +impl Future for OptionalFuture { + type Output = F::Output; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + match self.project().inner.as_pin_mut() { + Some(x) => x.poll(cx), + None => Poll::Pending, + } + } +} + +impl FusedFuture for OptionalFuture { + fn is_terminated(&self) -> bool { + match &self.inner { + Some(x) => x.is_terminated(), + None => true, + } + } +} + +impl From> for OptionalFuture { + fn from(option: Option) -> Self { + Self { inner: option } + } +}