Macro anony::try_join_cyclic
source · try_join_cyclic!() { /* proc-macro */ }
future
only.Expand description
Returns a future that “joins” multiple futures that will be completed concurrently, using cycling polling strategy. May short-circuit.
Usage note: If you are not sure which one to use (between this macro with try_join!
), just use the latter.
It is similar to join_cyclic!
, except it resolves to “continue” value if all the futures resolve to “continue” value,
and resolves to “break” value if one of the futures resolves to “break” value. The “continue” and the “break” value
are dependent on the output type of all the futures.
Here’s a basic overview of possible return types and return values:
(F0::Output, F1::Output, ...) | TryJoinCyclic::Output | “Continue” value | “Break” value | Note |
---|---|---|---|---|
(Option<T0>, Option<T1>, ...) | Option<(T0, T1, ...)> | Some | None | |
(Result<T0, E>, Result<T1, E>, ...) | Result<(T0, T1, ...), E> | Ok | Err | All errors must exactly be the same |
(ControlFlow<B, C0>, ControlFlow<B, C1>, ...) | ControlFlow<B, (C0, C1, ...)> | Continue | Break |
Formally, the trait bound is as the following, in term of Try
and Residual
traits:
impl<
F0: Future,
F1: Future,
...
FN: Future,
R: Residual<(
<F0::Output as Try>::Output,
<F1::Output as Try>::Output,
...
<FN::Output as Try>::Output,
)>,
> Future for TryJoinCyclic<F0, F1, ..., FN, R>
where
F0::Output: Try<Residual = R>,
F1::Output: Try<Residual = R>,
...
FN::Output: Try<Residual = R>,
{
type Output = R::TryType;
}
It means that in theory, you can use Poll<Result<T, E>>
and Poll<Option<Result<T, E>>>
and mix up
with other futures returning Result<T, E>
! However, to prevent such mess, the two types are NOT allowed
as of now. It may be lifted later when many unstable try_*
methods/functions are stabilized.
If the standard library add more types implementing Try
or Residual
,
this macro will NOT be aware of it. You can only be waiting till this crate is updated, or both traits are stabilized.
§DISCLAIMER
This macro does NOT use nightly or beta channel. It is usable on stable release.
§Possible differences from other implementations
-
try_join_cyclic!
returns an instance of an anonymous type implementedFuture
instead of requiring it to be inside anasync
. You will be warned if you neither.await
,poll
, nor return it. -
input futures are required to implement
IntoFuture
, and their outputs can be more than justResult
(see the first section above for the supported types). -
the returned future (generally) has smaller size and is (generally) faster.
-
the returned future is
Unpin
if all of the input futures areUnpin
.
§Examples
use anony::try_join_cyclic;
let a = async { Some(1) };
let b = async { Some(2) };
let c = async { Some(3) };
assert_eq!(try_join_cyclic!(a, b, c).await, Some((1, 2, 3)));
let a = async { Ok(4) };
let b = async { Err::<(), _>("5") };
assert_eq!(try_join_cyclic!(a, b).await, Err("5"));
let a = async { Some("6") };
let b = async { None::<&str> };
let c = async { Some("7") };
assert_eq!(try_join_cyclic!(a, b, c).await, None);
If you want to run a future (or more) while doing something else, this macro is a help! Note that you must put the “something else” after every other futures you want to run:
use anony::try_join_cyclic;
use tokio::time::sleep;
use std::time::Duration;
async fn read_db() -> Result<String, Box<dyn Error>> {
sleep(Duration::from_secs(1)).await;
Ok("My secret".into())
}
let (secret_value, _) = try_join_cyclic!(read_db(), async {
// Your other tasks go here, maybe asynchronous or just blocking...
let a = 1;
let b = 2;
assert_eq!(a + b, 3);
Ok(())
}).await?;
assert_eq!(secret_value, "My secret");