Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create a Future from a Result containing a Future?

Tags:

rust

future

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.

like image 816
mallwright Avatar asked Dec 17 '20 14:12

mallwright


People also ask

What is future Dart?

What is a future? A future (lower case “f”) is an instance of the Future (capitalized “F”) class. A future represents the result of an asynchronous operation, and can have two states: uncompleted or completed. Note: Uncompleted is a Dart term referring to the state of a future before it has produced a value.

How do you wait for a future to complete a flutter?

wait<T> method Null safety Returns a future which will complete once all the provided futures have completed, either with their results, or with an error if any of the provided futures fail.

What is a future in Rust?

Futures in rust allow you to define a task, like a network call or computation, to be run asynchronously. You can chain functions onto that result, transform it, handle errors, merge it with other futures, and perform many other computations on it.

What does Future mean in flutter?

A Future is used to represent a potential value, or error, that will be available at some time in the future. Receivers of a Future can register callbacks that handle the value or error once it is available. For example: Future<int> future = getFuture(); future. then((value) => handleValue(value)) .


2 Answers

Probably what one usually wants is to deal with the Err case immediately rather than waiting until the future is .awaited, 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).

like image 147
trent Avatar answered Sep 27 '22 18:09

trent


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) })
  }
}
like image 20
Ibraheem Ahmed Avatar answered Sep 27 '22 20:09

Ibraheem Ahmed