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}