9

I would like to run something like the following code:

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

But it doesn't work because await can only be used inside async blocks or functions. If, on the other hand, I use an async block, the closure would return impl Future<Output = Option<_>> which is not allowed because Option::and_then only allows Option as a return type.

The only alternative I know would be to use OptionFuture, create an intermediate variable and .map instead of .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);

But this is really cumbersome and prevents me from using many functions that would operate on Options, Results or similar, such as Option::and_then.

Is there no better way?

Filippo Orrù
  • 195
  • 3
  • 8

1 Answers1

3

I believe the idiomatic way would be to simply use match:

let user_id = match session_token {
    Some(session_token) => get_user("sessiontoken").await.map(|user| user.id),
    None => None,
};
kmdreko
  • 42,554
  • 6
  • 57
  • 106
  • I was really expecting some kind of transpose method. `Option>` -> `Future – Brandon Dyer Sep 16 '22 at 19:22
  • @BrandonDyer I would definitely appreciate it if `Option` were more ergonomic, but alas that is not the case and nothing has changed recently AFAIK. The Rust team may be waiting on [`IntoFuture`](https://doc.rust-lang.org/std/future/trait.IntoFuture.html) to stabilize since I believe there was potential confusion if `Option` implemented `Future` directly. Also related: [How do I transpose a Future out of an Option?](/q/69154566/2189130) – kmdreko Sep 16 '22 at 19:42