rust 如何在不使用OptionFuture的情况下在`Option::and_then`或`Option::map`的闭包中使用alloc/await?

hivapdat  于 2023-10-20  发布在  其他
关注(0)|答案(2)|浏览(80)

我想运行类似以下代码的东西:

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只能在JavaScript块或函数中使用。另一方面,如果我使用了一个basec块,闭包将返回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
没有更好的办法了吗?

wj8zmpe1

wj8zmpe11#

我相信惯用的方法是简单地使用match

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

wwodge7n2#

虽然match是惯用的,但也可以编写自己的combinator:

#[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

相关问题