如何在`Option::and_then`或`Option::map`的闭包中使用async/await,而不使用OptionFuture?

9

我想要运行类似以下代码的内容:

async fn get_user(s: &str) -> Option<User> { /* ... */ }

let user_id = Some("sessiontoken").and_then(|session_token| {
    get_user(session_token)
        .await // <- error
        .map(|user| user.id)
});

但这样不行,因为await只能在async块或函数中使用。相反,如果我使用async块,闭包将返回impl Future<Output = Option<_>>,这是不允许的,因为Option::and_then仅允许Option作为返回类型。
我所知道的唯一替代方案是使用OptionFuture,创建一个中间变量并使用.map而不是.and_then
use futures::future::OptionFuture;

let user: OptionFuture<_> = Some("sessiontoken")
    .map(|session_token| {
        get_user(session_token)
    })
    .into();
let user_id = user.await.flatten().map(|user| user.id);

但是这样真的很繁琐,而且阻止了我使用许多操作选项、结果或类似内容的函数,比如Option::and_then

难道没有更好的方法吗?

2个回答

4

我认为惯用的方法是简单地使用match

let user_id = match session_token {
    Some(session_token) => get_user("sessiontoken").await.map(|user| user.id),
    None => None,
};

我真的期望有某种转置方法。Option<Future<T>> -> Future<Option<T>>。然后你可以做类似于session_token.transpose().await这样的事情。 - Brandon Dyer
@BrandonDyer 如果 Option<Future> 更加符合人体工程学,我肯定会很感激,但不幸的是最近没有任何改变,至少在我看来是这样的。Rust 团队可能正在等待 IntoFuture 稳定下来,因为我相信如果 Option<Future> 直接实现了 Future,可能会存在潜在的混淆。此外,相关问题:如何从 Option 中转换出一个 Future? - kmdreko

0
虽然“match”是惯用语,但也有可能编写自己的组合器。
#[async_trait]
trait AsyncMap<T, U, F>
where
    F: FnOnce(T) -> Pin<Box<dyn Future<Output = U> + Send>> + Send,
{
    type Output;
    async fn async_map(self, map: F) -> Self::Output;
}

#[async_trait]
impl<T, U, F> AsyncMap<T, U, F> for Option<T>
where
    T: Send,
    U: Send,
    F: 'static + FnOnce(T) -> Pin<Box<dyn Future<Output = U> + Send>> + Send,
{
    type Output = Option<U>;
    async fn async_map(self, map: F) -> Self::Output {
        match self {
            Some(t) => {
                let u = map(t).await;
                Some(u)
            }
            None => None,
        }
    }
}

只要你在作用域中有AsyncMap,你就可以像这样使用它。
let foo = Some(123);
let bar = foo.as_ref().async_map(|value| Box::pin(async move {
    // probably in real life you would do something async here
    value.to_string() 
})).await;
assert_eq!(bar.unwrap(), "123");

playground


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