#future #tokio #context #convert #async-io

async-compat

tokio 和 futures 之间的兼容性适配器

10 个版本

0.2.4 2024 年 6 月 1 日
0.2.3 2023 年 10 月 18 日
0.2.2 2023 年 9 月 25 日
0.2.1 2021 年 3 月 20 日
0.1.3 2020 年 8 月 31 日

#522 in 异步

Download history 45643/week @ 2024-04-30 38020/week @ 2024-05-07 47695/week @ 2024-05-14 46486/week @ 2024-05-21 53688/week @ 2024-05-28 45729/week @ 2024-06-04 41999/week @ 2024-06-11 48185/week @ 2024-06-18 50567/week @ 2024-06-25 40734/week @ 2024-07-02 38665/week @ 2024-07-09 36200/week @ 2024-07-16 39558/week @ 2024-07-23 41325/week @ 2024-07-30 35709/week @ 2024-08-06 41012/week @ 2024-08-13

164,495 个月下载
用于 142 个 crate (64 个直接使用)

Apache-2.0 OR MIT

24KB
245

async-compat

Build License Cargo Documentation

tokio 和 futures 之间的兼容性适配器。

tokio 和 futures 之间有两种兼容性问题

  1. Tokio 的类型不能在 tokio 上下文之外使用,所以任何尝试使用它们的操作都会导致 panic。
    • 解决方案:如果您将 Compat 适配器应用于一个 future,该 future 将进入由此 crate 启动的全局单线程 tokio 运行时的上下文。这并不意味着 future 在 tokio 运行时上运行 - 这只意味着 future 设置了一个指向全局 tokio 运行时的线程局部变量,以便在内部可以使用 tokio 的类型。
  2. Tokio 和 futures 有相似但不同的 I/O 特性 AsyncReadAsyncWriteAsyncBufReadAsyncSeek
    • 解决方案:当 Compat 适配器应用于 I/O 类型时,它将实现相反类型的特性行为。这就是您可以在需要基于 futures 的类型的地方使用基于 tokio 的类型,反之亦然的原因。

示例

此程序从 stdin 读取行并将它们回显到 stdout,但它不会正常工作

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(())
    })
}

为了解决兼容性问题,将 Compat 适配器应用于 stdinstdoutfutures::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(())
    })
}

还可以将 Compat 应用于传递给 futures::executor::block_on() 的外部 future,而不是 futures::io::copy() 本身。当应用于外部 future 时,单个内部 future 不需要适配器,因为它们现在都在 tokio 上下文中

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(())
    }))
}

兼容适配器可以在任意方向上在基于tokio和基于futures的I/O类型之间进行转换。以下是使用tokio内部基于futures的I/O类型编写相同程序的方法

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(())
}

最后,我们可以使用任何基于tokio的crate来运行任何其他异步运行时。以下以reqwestwarp为例

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.net.cn").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;
    }))
}

许可协议

许可协议为以下之一

任选其一。

贡献

除非您明确说明,否则根据Apache-2.0许可协议定义的,您有意提交并包含在作品中的任何贡献,将按上述方式双许可,不附加任何额外条款或条件。

依赖项

~2–3MB
~48K SLoC