Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I use async/await syntax with Tokio?

I'm trying to use async/await with processes in Rust. I'm using tokio and tokio-process:

#![feature(await_macro, async_await, futures_api)]

extern crate tokio;
extern crate tokio_process;

use std::process::Command;
use tokio_process::CommandExt;

fn main() {
    tokio::run_async(main_async());
}

async fn main_async() {
    let out = Command::new("echo")
        .arg("hello")
        .arg("world")
        .output_async();
    let s = await!(out);
}

Here is the error that I get:

error[E0277]: the trait bound `tokio_process::OutputAsync: std::future::Future` is not satisfied
  --> src/main.rs:21:13
   |
21 |     let s = await!(out);
   |             ^^^^^^^^^^^ the trait `std::future::Future` is not implemented for `tokio_process::OutputAsync`
   |
   = note: required by `std::future::poll_with_tls_waker`
   = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)

error[E0277]: the trait bound `tokio_process::OutputAsync: std::future::Future` is not satisfied
  --> src/main.rs:21:13
   |
21 |     let s = await!(out);
   |             ^^^^^^^^^^^ the trait `std::future::Future` is not implemented for `tokio_process::OutputAsync`
   |
   = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)

How do I get this right?

like image 956
Hristo Kolev Avatar asked Feb 24 '19 16:02

Hristo Kolev


1 Answers

TL;DR: Use Tokio 0.2 or newer and it should just work.

Tokio 0.1 and related crates are implemented using the futures 0.1 crate. The Future trait from this crate is conceptually similar to the version of the Future trait from the standard library but substantially different in details. async / await syntax is built around the version of the trait in the standard library.

Tokio 0.2 and related crates are implemented using the standard library Future and have been reworked to better support async / await syntax.

Tokio 0.2

[dependencies]
tokio = { version = "0.2", features = ["full"] }
use tokio::process::Command;

#[tokio::main]
async fn main() -> std::io::Result<()> {
    let out = Command::new("echo")
        .arg("hello")
        .arg("world")
        .output()
        .await?;

    let s = String::from_utf8_lossy(&out.stdout);

    println!("{}", s);
    Ok(())
}
$ cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.05s
     Running `target/debug/tokio2`
hello world

Tested with:

  • Rustc 1.39.0

Tokio 0.1

You have to translate between a Future implemented against the trait from the futures 0.1 crate and from the standard library. In addition, you need to start up the Tokio runtime services, ideally for both Tokio 0.1 and Tokio 0.3. That's where tokio-compat comes in:

[dependencies]
futures = { version = "0.3", features = ["compat"] }
tokio-compat = "0.1"
tokio-process = "0.2"
use futures::compat::Future01CompatExt;
use std::process::Command;
use tokio_process::CommandExt;

fn main() {
    tokio_compat::run_std(main_async());
}

async fn main_async() {
    let out = Command::new("echo")
        .arg("hello")
        .arg("world")
        .output_async();

    // Convert future from 0.1 to 0.3
    let s = out.compat().await;

    println!("{:?}", s);
}
% cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.07s
     Running `target/debug/tt`
Ok(Output { status: ExitStatus(ExitStatus(0)), stdout: "hello world\n", stderr: "" })

Tested with Rust 1.43.0

See also:

  • How do I convert an async / standard library future to futures 0.1?
  • Is there a way that we can convert from futures 0.1 to the standard library futures?
  • Why is a trait not implemented for a type that clearly has it implemented?
like image 154
Shepmaster Avatar answered Oct 14 '22 08:10

Shepmaster