如何从包含 Future 的 Result 中创建一个 Future?

8

能否(逻辑上)将包含未来结果的结果转换为解析为结果的未来?

下面的函数有点问题,但希望它能更清晰地表达我想要实现的目标:

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) */
   }
}

为了提供一些背景:我发现自己需要在调用从传递给Result::map的闭包中调用的async函数之后执行此操作,因此这可能是我的第一个错误。

2个回答

3

可能通常情况下,人们希望立即处理Err情况,而不是等待未来被.await时才处理,这也许可以解释为什么这样的函数还不存在。但是,如果您确实需要,使用async.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
        }
    }
}

或者使用async fn来实现同样的功能(虽然在某些情况下这两种方式并不完全相同,但在本例中似乎是可以的):

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),
    }
}

由于Err值是从包含的async块或函数返回的,因此您可以使用?语法使其更简短:

async fn transpose_flatten<T, U, E>(result: Result<T, E>) -> Result<U, E>
where
    T: Future<Output = Result<U, E>>,
{
    result?.await
}

我称这个函数为transpose_flatten,因为对我来说transpose听起来应该接收一个Result<Future<Output = U>, _>。 这个函数将两层Result(传递给函数的和从future返回的)展平。


我认为OP想要将结果转换为Future,而.await实际上是解析Future,这不仅是转换。 - Ibraheem Ahmed
1
@Ibraheem 我认为你忽略了 async 块。当调用 transpose_flatten 时,.await 不会运行。 - trent
我认为可以通过使用正确的函数式编程方法避免“Err(bad) => Err(bad)”这一行。我猜这不是情况吗? - mallwright
1
@MichaelAllwright 一般情况下你不能避免它。显而易见的想法,result.map(|good| good.await),不起作用,因为.await是一个控制运算符,影响着直接包围它的async函数,所以你不能将其包装在闭包中(就像return一样)。然而,由于match的结果已经被返回,你可以使用?来传播错误值:result?.await与此处的match执行相同的操作(但如果它不是函数的最后一个表达式语句,则不会执行相同的操作)。 - trent
1
我已将这个变体添加到答案中。我要指出的是,它并没有让我特别感觉到"函数式",但我认为这是一个无果的争论。如果代码简洁,工作正常,并且对您而言有意义,那么谁会在乎它是否符合"函数式编程"的标准呢? - trent

1
如果resultOk,你可以简单地返回内部的future。然而,如果它是Err,那么你必须构造一个新的future,解析为Result<U, E>::Err(e)。这意味着你不能返回一个通用的Future,因为这两个match分支返回了两种不同的类型。你必须返回一个特质对象:
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) })
  }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接