The new std::future::Future common combinators are mush more complicated than I thought. Here is another one join_on_ok will run two Result returning Futures in parallel, and stop if either of then results in Err, or both of them results in Ok.
With the EitherOr defined before, I wrote
pub fn join_on_ok<T1, T2, E1, E2>(
f1: impl Future<Output = Result<T1, E1>>,
f2: impl Future<Output = Result<T2, E2>>,
) -> impl Future<Output = EitherOr<Result<T1,E1>,Result<T2,E2>>> {
struct JoinFuture<T1, T2, F1, F2>(F1, F2, Option<T1>, Option<T2>);
impl<T1, T2, E1, E2, F1, F2> Future for JoinFuture<T1, T2, F1, F2>
where
F1: Future<Output = Result<T1, E1>>,
F2: Future<Output = Result<T2, E2>>,
{
type Output = EitherOr<Result<T1,E1>,Result<T2,E2>>;
fn poll(self: Pin<&mut Self>, lw: &LocalWaker) -> Poll<Self::Output> {
let this = unsafe {
let this = self.get_unchecked_mut();
(
Pin::new_unchecked(&mut this.0),
Pin::new_unchecked(&mut this.1),
&mut this.2,
&mut this.3,
)
};
match (&this.2, &this.3) {
(Some(_), Some(_)) => unreachable!(),
(Some(_), _) => match this.1.poll(lw) {
Ready(Ok(v2)) => Ready(EitherOr::Both(Ok(this.2.take().unwrap()), Ok(v2))),
Ready(Err(e2)) => Ready(EitherOr::Both(Ok(this.2.take().unwrap()), Err(e2))),
_ => Pending,
},
(_, Some(_)) => match this.0.poll(lw) {
Ready(Ok(v1)) => Ready(EitherOr::Both(Ok(v1), Ok(this.3.take().unwrap()))),
Ready(Err(e1)) => Ready(EitherOr::Both(Err(e1), Ok(this.3.take().unwrap()))),
_ => Pending,
},
_ => match (this.0.poll(lw), this.1.poll(lw)) {
(Ready(Ok(v1)), Ready(Ok(v2))) => Ready(EitherOr::Both(Ok(v1), Ok(v2))),
(Ready(Err(e1)), Ready(Ok(v2))) => Ready(EitherOr::Both(Err(e1), Ok(v2))),
(Ready(Ok(v1)), Ready(Err(e2))) => Ready(EitherOr::Both(Ok(v1), Err(e2))),
(Ready(Err(e1)), Ready(Err(e2))) => Ready(EitherOr::Both(Err(e1), Err(e2))),
(Ready(Ok(v1)), Pending) => {
*this.2 = Some(v1);
Pending
},
(Ready(Err(e1)), _) => Ready(EitherOr::This(Err(e1))),
(Pending, Ready(Ok(v2))) => {
*this.3 = Some(v2);
Pending
},
(_, Ready(Err(e2))) => Ready(EitherOr::That(Err(e2))),
_ => Pending,
},
}
}
}
JoinFuture(f1, f2, None, None)
}
This combinator is natually appear when I have to proxy connection. In such a case I hold 2 seperate data transfering futures in the form of source -> proxy -> target, and if either futures are stopped I need to stop the other one as well, or they can both close peacefully.
Right now, I identified 4 combinators that cannot be written in await! form, and requires manual implementing:
-
join: run two Futures in parallel, stop if both stops.
-
alt: run two Futures in parrel, stop if either or both of them stops.
-
select, a varition of alt, where will be bias to one of the Futures, such that other Futures wil not be polled in the same circle.
-
join_on_ok: a variation of join such that the outputs are Results, and the stop criteria is either of the futures fail or both success.