Crates.io | async-compat |
lib.rs | async-compat |
version | 0.2.4 |
source | src |
created_at | 2020-08-30 20:08:12.976067 |
updated_at | 2024-06-01 14:25:20.598243 |
description | Compatibility adapter between tokio and futures |
homepage | https://github.com/smol-rs/async-compat |
repository | https://github.com/smol-rs/async-compat |
max_upload_size | |
id | 282816 |
size | 38,558 |
Compatibility adapter between tokio and futures.
There are two kinds of compatibility issues between tokio and futures:
Compat
adapter to a future, the future will enter the
context of a global single-threaded tokio runtime started by this crate. That does
not mean the future runs on the tokio runtime - it only means the future sets a
thread-local variable pointing to the global tokio runtime so that tokio's types can be
used inside it.AsyncRead
, AsyncWrite
,
AsyncBufRead
, and AsyncSeek
.
Compat
adapter is applied to an I/O type, it will implement traits
of the opposite kind. That's how you can use tokio-based types wherever futures-based
types are expected, and the other way around.This program reads lines from stdin and echoes them into stdout, except it's not going to work:
fn main() -> std::io::Result<()> {
futures::executor::block_on(async {
let stdin = tokio::io::stdin();
let mut stdout = tokio::io::stdout();
// The following line will not work for two reasons:
// 1. Runtime error because stdin and stdout are used outside tokio context.
// 2. Compilation error due to mismatched `AsyncRead` and `AsyncWrite` traits.
futures::io::copy(stdin, &mut stdout).await?;
Ok(())
})
}
To get around the compatibility issues, apply the Compat
adapter to stdin
, stdout
, and
futures::io::copy()
:
use async_compat::CompatExt;
fn main() -> std::io::Result<()> {
futures::executor::block_on(async {
let stdin = tokio::io::stdin();
let mut stdout = tokio::io::stdout();
futures::io::copy(stdin.compat(), &mut stdout.compat_mut()).compat().await?;
Ok(())
})
}
It is also possible to apply Compat
to the outer future passed to
futures::executor::block_on()
rather than futures::io::copy()
itself.
When applied to the outer future, individual inner futures don't need the adapter because
they're all now inside tokio context:
use async_compat::{Compat, CompatExt};
fn main() -> std::io::Result<()> {
futures::executor::block_on(Compat::new(async {
let stdin = tokio::io::stdin();
let mut stdout = tokio::io::stdout();
futures::io::copy(stdin.compat(), &mut stdout.compat_mut()).await?;
Ok(())
}))
}
The compatibility adapter converts between tokio-based and futures-based I/O types in any direction. Here's how we can write the same program by using futures-based I/O types inside tokio:
use async_compat::CompatExt;
use blocking::Unblock;
#[tokio::main]
async fn main() -> std::io::Result<()> {
let mut stdin = Unblock::new(std::io::stdin());
let mut stdout = Unblock::new(std::io::stdout());
tokio::io::copy(&mut stdin.compat_mut(), &mut stdout.compat_mut()).await?;
Ok(())
}
Finally, we can use any tokio-based crate from any other async runtime. Here are reqwest and warp as an example:
use async_compat::{Compat, CompatExt};
use warp::Filter;
fn main() {
futures::executor::block_on(Compat::new(async {
// Make an HTTP GET request.
let response = reqwest::get("https://www.rust-lang.org").await.unwrap();
println!("{}", response.text().await.unwrap());
// Start an HTTP server.
let routes = warp::any().map(|| "Hello from warp!");
warp::serve(routes).run(([127, 0, 0, 1], 8080)).await;
}))
}
Licensed under either of
at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.