1use std::{
2 fmt,
3 future::Future,
4 pin::Pin,
5 task::{Context, Poll},
6};
7
8pub fn join<A: Future, B: Future>(a: A, b: B) -> Join<A, B> {
14 Join {
15 a: MaybeDone::new(a),
16 b: MaybeDone::new(b),
17 }
18}
19
20pub fn try_join<T1, T2, E, A, B>(a: A, b: B) -> TryJoin<A, B>
27where
28 A: Future<Output = Result<T1, E>>,
29 B: Future<Output = Result<T2, E>>,
30{
31 TryJoin {
32 a: MaybeDone::new(a),
33 b: MaybeDone::new(b),
34 }
35}
36
37struct MaybeDone<F: Future> {
39 fut: Option<Pin<Box<F>>>,
40 output: Option<F::Output>,
41}
42
43impl<F: Future> MaybeDone<F> {
44 fn new(f: F) -> Self {
45 Self {
46 fut: Some(Box::pin(f)),
47 output: None,
48 }
49 }
50
51 fn poll(&mut self, cx: &mut Context<'_>) -> bool {
53 if let Some(fut) = self.fut.as_mut()
54 && let Poll::Ready(out) = fut.as_mut().poll(cx)
55 {
56 self.output = Some(out);
57 self.fut = None;
58 }
59 self.fut.is_none()
60 }
61
62 fn is_done(&self) -> bool {
63 self.fut.is_none()
64 }
65
66 fn take(&mut self) -> F::Output {
67 self.output.take().expect("take on a not-yet-done future")
68 }
69}
70
71impl<T, E, F: Future<Output = Result<T, E>>> MaybeDone<F> {
72 fn poll_err(&mut self, cx: &mut Context<'_>) -> Option<E> {
74 if !self.poll(cx) || !matches!(self.output, Some(Err(_))) {
75 return None;
76 }
77 self.take().err()
78 }
79}
80
81impl<F: Future> fmt::Debug for MaybeDone<F> {
82 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
83 f.debug_struct("MaybeDone")
84 .field("done", &self.is_done())
85 .finish()
86 }
87}
88
89#[must_use = "futures do nothing unless you `.await` or poll them"]
91pub struct Join<A: Future, B: Future> {
92 a: MaybeDone<A>,
93 b: MaybeDone<B>,
94}
95
96impl<A: Future, B: Future> fmt::Debug for Join<A, B> {
99 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
100 f.debug_struct("Join")
101 .field("a", &self.a)
102 .field("b", &self.b)
103 .finish()
104 }
105}
106
107impl<A: Future, B: Future> Future for Join<A, B> {
108 type Output = (A::Output, B::Output);
109
110 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
111 let this = self.get_mut();
113 let a_done = this.a.poll(cx);
114 let b_done = this.b.poll(cx);
115 if a_done && b_done {
116 Poll::Ready((this.a.take(), this.b.take()))
117 } else {
118 Poll::Pending
119 }
120 }
121}
122
123impl<A: Future, B: Future> Unpin for Join<A, B> {}
125
126#[must_use = "futures do nothing unless you `.await` or poll them"]
128pub struct TryJoin<A: Future, B: Future> {
129 a: MaybeDone<A>,
130 b: MaybeDone<B>,
131}
132
133impl<A: Future, B: Future> fmt::Debug for TryJoin<A, B> {
134 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
135 f.debug_struct("TryJoin")
136 .field("a", &self.a)
137 .field("b", &self.b)
138 .finish()
139 }
140}
141
142impl<A: Future, B: Future> Unpin for TryJoin<A, B> {}
143
144impl<T1, T2, E, A, B> Future for TryJoin<A, B>
145where
146 A: Future<Output = Result<T1, E>>,
147 B: Future<Output = Result<T2, E>>,
148{
149 type Output = Result<(T1, T2), E>;
150
151 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
152 let this = self.get_mut();
153
154 if let Some(err) = this.a.poll_err(cx) {
157 return Poll::Ready(Err(err));
158 }
159 if let Some(err) = this.b.poll_err(cx) {
160 return Poll::Ready(Err(err));
161 }
162
163 if this.a.is_done() && this.b.is_done() {
164 let a = this.a.take().ok().expect("checked Ok");
165 let b = this.b.take().ok().expect("checked Ok");
166 Poll::Ready(Ok((a, b)))
167 } else {
168 Poll::Pending
169 }
170 }
171}
172
173#[cfg(test)]
174mod tests {
175 use super::*;
176 use crate::util::simple_block_on;
177 use std::{cell::Cell, future::poll_fn, rc::Rc};
178
179 fn delayed<T: Clone + 'static>(pendings: usize, val: T) -> impl Future<Output = T> {
181 let left = Rc::new(Cell::new(pendings));
182 poll_fn(move |cx: &mut Context<'_>| {
183 if left.get() == 0 {
184 Poll::Ready(val.clone())
185 } else {
186 left.set(left.get() - 1);
187 cx.waker().wake_by_ref();
188 Poll::Pending
189 }
190 })
191 }
192
193 #[test]
196 fn combinators_are_debug_over_async_blocks() {
197 fn debug<T: fmt::Debug>(t: &T) -> String {
198 format!("{t:?}")
199 }
200 debug(&join(async { 1u8 }, async { 2u8 }));
201 debug(&try_join(async { Ok::<u8, ()>(1) }, async {
202 Ok::<u8, ()>(2)
203 }));
204 }
205
206 #[test]
207 fn join_returns_both() {
208 let (a, b) = simple_block_on(join(delayed(2, 1u8), delayed(5, "x")));
209 assert_eq!(a, 1);
210 assert_eq!(b, "x");
211 }
212
213 #[test]
214 fn try_join_ok_returns_both() {
215 let out: Result<(u8, u8), ()> =
216 simple_block_on(try_join(delayed(1, Ok(1u8)), delayed(3, Ok(2u8))));
217 assert_eq!(out, Ok((1, 2)));
218 }
219
220 #[test]
221 fn try_join_short_circuits_on_error() {
222 let other = poll_fn(|cx: &mut Context<'_>| {
225 cx.waker().wake_by_ref();
226 Poll::<Result<u8, &str>>::Pending
227 });
228 let err = delayed(1, Err::<u8, &str>("boom"));
229 let out = simple_block_on(try_join(err, other));
230 assert_eq!(out, Err("boom"));
231 }
232}