use pin_project_lite::pin_project; use super::{task, Future, Pin, Poll}; pub(crate) trait Started: Future { fn started(&self) -> bool; } pub(crate) fn lazy(func: F) -> Lazy where F: FnOnce() -> R, R: Future + Unpin, { Lazy { inner: Inner::Init { func }, } } // FIXME: allow() required due to `impl Trait` leaking types to this lint pin_project! { #[allow(missing_debug_implementations)] pub(crate) struct Lazy { #[pin] inner: Inner, } } pin_project! { #[project = InnerProj] #[project_replace = InnerProjReplace] enum Inner { Init { func: F }, Fut { #[pin] fut: R }, Empty, } } impl Started for Lazy where F: FnOnce() -> R, R: Future, { fn started(&self) -> bool { match self.inner { Inner::Init { .. } => false, Inner::Fut { .. } | Inner::Empty => true, } } } impl Future for Lazy where F: FnOnce() -> R, R: Future, { type Output = R::Output; fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll { let mut this = self.project(); if let InnerProj::Fut { fut } = this.inner.as_mut().project() { return fut.poll(cx); } match this.inner.as_mut().project_replace(Inner::Empty) { InnerProjReplace::Init { func } => { this.inner.set(Inner::Fut { fut: func() }); if let InnerProj::Fut { fut } = this.inner.project() { return fut.poll(cx); } unreachable!() } _ => unreachable!("lazy state wrong"), } } }