async_macros/
try_join.rs

1/// Awaits multiple fallible futures simultaneously, returning all results once
2/// complete.
3///
4/// `try_join!` is similar to [`join!`], but completes immediately if any of
5/// the futures return an error.
6///
7/// This macro is only usable inside of async functions, closures, and blocks.
8///
9/// # Examples
10///
11/// When used on multiple futures that return `Ok`, `try_join!` will return
12/// `Ok` of a tuple of the values:
13///
14/// ```
15/// #![feature(async_await)]
16/// # futures::executor::block_on(async {
17/// use async_macros::try_join;
18/// use futures::future;
19///
20/// let a = future::ready(Ok::<i32, i32>(1));
21/// let b = future::ready(Ok::<u64, i32>(2));
22///
23/// assert_eq!(try_join!(a, b).await, Ok((1, 2)));
24/// # });
25/// ```
26///
27/// If one of the futures resolves to an error, `try_join!` will return
28/// that error:
29///
30/// ```
31/// #![feature(async_await)]
32/// # futures::executor::block_on(async {
33/// use async_macros::try_join;
34/// use futures::future;
35///
36/// let a = future::ready(Ok::<i32, i32>(1));
37/// let b = future::ready(Err::<u64, i32>(2));
38///
39/// assert_eq!(try_join!(a, b).await, Err(2));
40/// # });
41/// ```
42#[macro_export]
43macro_rules! try_join {
44    ($($fut:ident),* $(,)?) => { {
45        async {
46            use $crate::utils::future::Future;
47            use $crate::utils::pin::Pin;
48            use $crate::utils::poll_fn;
49            use $crate::utils::result::Result;
50            use $crate::utils::task::Poll;
51
52            $(
53                // Move future into a local so that it is pinned in one place and
54                // is no longer accessible by the end user.
55                let mut $fut = $crate::MaybeDone::new($fut);
56            )*
57
58            let res: Result<_, _> = poll_fn(move |cx| {
59                let mut all_done = true;
60                $(
61                    let fut = unsafe { Pin::new_unchecked(&mut $fut) };
62                    if Future::poll(fut, cx).is_pending() {
63                        all_done = false;
64                    } else if unsafe { Pin::new_unchecked(&mut $fut) }.output_mut().unwrap().is_err() {
65                        // `.err().unwrap()` rather than `.unwrap_err()` so that we don't introduce
66                        // a `T: Debug` bound.
67                        return Poll::Ready(
68                            Result::Err(unsafe { Pin::new_unchecked(&mut $fut) }
69                                .take()
70                                .unwrap()
71                                .err()
72                                .unwrap()
73                        ));
74                    }
75                )*
76                if all_done {
77                    let res = ($(
78                        // `.ok().unwrap()` rather than `.unwrap()` so that we don't introduce
79                        // an `E: Debug` bound.
80                        unsafe { Pin::new_unchecked(&mut $fut) }
81                            .take()
82                            .unwrap()
83                            .ok()
84                            .unwrap(),
85                    )*);
86                    Poll::Ready(Result::Ok(res))
87                } else {
88                    Poll::Pending
89                }
90            }).await;
91            res
92        }
93    } }
94}