diff --git a/src/future.rs b/src/future.rs index 402897e..5bb96e3 100644 --- a/src/future.rs +++ b/src/future.rs @@ -453,6 +453,66 @@ where } } +/// Fuse a future such that `poll` will never again be called once it has +/// completed. This method can be used to turn any `Future` into a +/// `FusedFuture`. +/// +/// Normally, once a future has returned `Poll::Ready` from `poll`, +/// any further calls could exhibit bad behavior such as blocking +/// forever, panicking, never returning, etc. If it is known that `poll` +/// may be called too often then this method can be used to ensure that it +/// has defined semantics. +/// +/// If a `fuse`d future is `poll`ed after having returned `Poll::Ready` +/// previously, it will return `Poll::Pending`, from `poll` again (and will +/// continue to do so for all future calls to `poll`). +/// +/// This combinator will drop the underlying future as soon as it has been +/// completed to ensure resources are reclaimed as soon as possible. +pub fn fuse(future: F) -> Fuse +where + F: Future + Sized, +{ + Fuse::new(future) +} + +pin_project! { + /// [`Future`] for the [`fuse`] method. + #[derive(Debug)] + #[must_use = "futures do nothing unless you `.await` or poll them"] + pub struct Fuse { + #[pin] + inner: Option, + } +} + +impl Fuse { + fn new(f: Fut) -> Self { + Self { inner: Some(f) } + } +} + +impl Future for Fuse { + type Output = Fut::Output; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + match self + .as_mut() + .project() + .inner + .as_pin_mut() + .map(|f| f.poll(cx)) + { + Some(Poll::Ready(output)) => { + self.project().inner.set(None); + Poll::Ready(output) + } + + Some(Poll::Pending) | None => Poll::Pending, + } + } +} + /// Returns the result of the future that completes first, with no preference if both are ready. /// /// Each time [`Race`] is polled, the two inner futures are polled in random order. Therefore, no