mr.celo
mr.celo

Reputation: 585

Is it possible to check if a future is ready, without consuming its output?

Futures in Rust can be polled without blocking to check if the future is ready (and do some work in the process). With that in mind, is it possible to "check" if a future is ready, without consuming its output?

Possible scenario

stdin would be polled if there is any data, and take action on the input (playground):

async fn read_input_if_available() {
    use tokio::io::AsyncReadExt;

    let mut stdin = tokio::io::stdin();

    // if !stdin.ready() {
    //    return;
    // }

    let mut input = String::new();
    let mut buffer = [0_u8; 1024];

    while let Ok(bytes) = stdin.read(&mut buffer).await {
        if let Ok(string) = std::str::from_utf8(&buffer[..bytes]) {
            input.push_str(string);
        }
    }

    // Take action on `input` here
}

When the code hits the await, it will not proceed until there is something on stdin, even if just waiting for an EOF.

I used tokio::io::Stdin because it is simpler for a self-contained example, but the question is about Rust futures in general.

Upvotes: 6

Views: 5630

Answers (1)

Shepmaster
Shepmaster

Reputation: 431669

No, not directly. Like iterators, futures and streams are one-shot: you can only get the result of poll or poll_next once.

Iterators have the peekable method and futures and streams have analogs:

In some cases, you may be able to use FutureExt::now_or_never combined with the implementation of Future for a mutable reference:

use futures::FutureExt; // 0.3.13
use std::time::Duration;
use tokio::time; // 1.3.0

#[tokio::main]
async fn main() {
    let mut f = something_else().boxed();

    if let Some(v) = (&mut f).now_or_never() {
        eprintln!("it was ready and it was {}", v);
    } else {
        time::sleep(Duration::from_millis(500)).await;

        let v = f.await;
        eprintln!("now it's ready and it was {}", v);
    }
}

async fn something_else() -> i32 {
    //time::sleep(Duration::from_millis(1000)).await;
    42
}

You do need to ensure that the future implements Unpin (done here via FutureExt::boxed and you must not .await a future after it has already finished.

Upvotes: 7

Related Questions