4

I was trying to use futures::future::select_ok (playground):

use std::time::Duration;
use tokio; // 1.16.1
use futures; // 0.3.19

#[tokio::main]
async fn main() {
    let first_future = async {
        tokio::time::sleep(Duration::from_secs(1)).await;
        Ok(3)
    };
    let second_future = async {
        tokio::time::sleep(Duration::from_millis(100)).await;
        Err(())
    };
    let third_future = async {
        tokio::time::sleep(Duration::from_secs(300)).await;
        Ok(3)
    };
    futures::future::select_ok(&[first_future,second_future.await,third_future]).await;
}

and encountered the error:

error[E0308]: mismatched types
  --> src/main.rs:19:47
   |
7  |       let first_future = async {
   |  ______________________________-
8  | |         tokio::time::sleep(Duration::from_secs(1)).await;
9  | |         Ok(3)
10 | |     };
   | |_____- the expected `async` block
...
19 |       futures::future::select_ok(&[first_future,second_future.await,third_future]).await;
   |                                                 ^^^^^^^^^^^^^^^^^^^ expected opaque type, found enum `Result`
   |
   = note: expected opaque type `impl futures::Future<Output = [async output]>`
                     found enum `Result<_, ()>`

For more information about this error, try `rustc --explain E0308`.
error: could not compile `playground` due to previous error

I have no idea what I'm missing here, would really appreciate any help.

0

2 Answers 2

6

Each of the async blocks are treated as a different type, the same way closures are (IIRC). All of them implements Future, so you may want to actually dynamically dispatch them. For that you need to wrap them in Pin<Box>:

use futures;
use std::future::Future;
use std::pin::Pin;
use std::time::Duration;
use tokio; // 1.16.1 // 0.3.19

#[tokio::main]
async fn main() {
    let first_future = async {
        tokio::time::sleep(Duration::from_secs(1)).await;
        Ok(3)
    };
    let second_future = async {
        tokio::time::sleep(Duration::from_millis(100)).await;
        Err(())
    };
    let third_future = async {
        tokio::time::sleep(Duration::from_secs(300)).await;
        Ok(3)
    };
    let futures: [Pin<Box<dyn Future<Output = Result<usize, ()>>>>; 3] = [
        Box::pin(first_future),
        Box::pin(second_future),
        Box::pin(third_future),
    ];
    futures::future::select_ok(futures).await;
}

Playground

Sign up to request clarification or add additional context in comments.

3 Comments

Note: this is partially unrelated to select_ok, because even trying to put values of different types (first_future and second_future) into an array is an error.
Do you know why &dyn or Box<dyn> won't work in this case, why do we need Pin<Box<dyn>>?
@battlmonstr, futures usually need to be pinned. It is the constrain that they will not be moved while making the computation.
0

This is in addition to the excellent answer by @Netwave:

In case someone is looking to avoid the heap allocation that Pin<Box<dyn ..>> requires, have a look at the enum_dispatch or smallbox crates, which enable stack-allocated differently-sized objects.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.