Reputation: 1975
Is it possible (logical?) to convert a result containing a future into a future that resolves to a result?
The following function is a bit broken, but hopefully makes what I am trying to achieve more clear:
use std::future::Future;
fn transpose<T,U,E>(result: Result<T,E>) -> impl Future<Output = Result<U, E>>
where T: Future<Output = Result<U,E>> /* not sure if this is the correct trait bound */ {
match result {
Ok(inner) => /* a future that eventually resolves to a Result<U, E> */
Err(error) => /* a future that immediately resolves to a Result<U, E>::Err(error) */
}
}
To give some context: I found myself needing to do this after calling a async
function from a closure passed to Result::map
, so perhaps that was my first mistake.
Upvotes: 8
Views: 2839
Reputation: 28075
Probably what one usually wants is to deal with the Err
case immediately rather than waiting until the future is .await
ed, so that might explain why a function like this doesn't already exist. However, if you do need it, it's pretty simple to write using async
and .await
:
fn transpose_flatten<T, U, E>(result: Result<T, E>) -> impl Future<Output = Result<U, E>>
where
T: Future<Output = Result<U, E>>,
{
async { // when polled,
match result {
Ok(good) => good.await, // defer to the inner Future if it exists
Err(bad) => Err(bad), // otherwise return the error immediately
}
}
}
Or the same thing using async fn
(which is not always quite the same thing, but in this case it seems to be):
async fn transpose_flatten<T, U, E>(result: Result<T, E>) -> Result<U, E>
where
T: Future<Output = Result<U, E>>,
{
match result {
Ok(good) => good.await,
Err(bad) => Err(bad),
}
}
Since the Err
value is returned from the enclosing async
block or function, you can use ?
syntax to make it even shorter:
async fn transpose_flatten<T, U, E>(result: Result<T, E>) -> Result<U, E>
where
T: Future<Output = Result<U, E>>,
{
result?.await
}
I called this function transpose_flatten
because to me transpose
sounds like it should take a Result<Future<Output = U>, _>
. This function flattens two layers of Result
(the one passed to the function and the one returned from the future).
Upvotes: 3
Reputation: 13618
If result
is Ok
you can simply return the inner future. However, if it is Err
, then you have to construct a new future resolving to Result<U, E>::Err(e)
. This means that you cannot return a generic Future
because the two match
arms return two distinct types. You have to return a trait object:
fn transpose<T, E: 'static, U: 'static>(result: Result<T, E>) -> Box<dyn Future<Output = Result<U, E>>>
where T: Future<Output = Result<U, E>> + 'static {
match result {
Ok(inner) => Box::new(inner),
Err(error) => Box::new(async { Err(error) })
}
}
Upvotes: 1