1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
|
# async-compat
[](
https://github.com/smol-rs/async-compat/actions)
[](
https://github.com/smol-rs/async-compat)
[](
https://crates.io/crates/async-compat)
[](
https://docs.rs/async-compat)
Compatibility adapter between tokio and futures.
There are two kinds of compatibility issues between [tokio] and [futures]:
2. Tokio's types cannot be used outside tokio context, so any attempt to use
them will panic.
- Solution: If you apply the `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.
2. Tokio and futures have similar but different I/O traits `AsyncRead`, `AsyncWrite`,
`AsyncBufRead`, and `AsyncSeek`.
- Solution: When the `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.
## Examples
This program reads lines from stdin and echoes them into stdout, except it's not going to work:
```rust
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()`]:
```rust
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:
```rust
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:
```rust
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:
```rust
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;
}))
}
```
[blocking]: https://docs.rs/blocking
[futures]: https://docs.rs/futures
[reqwest]: https://docs.rs/reqwest
[tokio]: https://docs.rs/tokio
[warp]: https://docs.rs/warp
[`futures::io::copy()`]: https://docs.rs/futures/0.3/futures/io/fn.copy.html
[`futures::executor::block_on()`]: https://docs.rs/futures/0.3/futures/executor/fn.block_on.html
## License
Licensed under either of
* Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
at your option.
#### Contribution
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.
|