2022 年的异步 Rust

2022 年 2 月 3 日 · Niko Matsakis 和 Tyler Mandry 代表 异步工作组

差不多一年前,异步工作组1 开始了一项协作工作,以编写一份共享的异步愿景文档。进入 2022 年之际,我们想就该过程的结果以及我们在实现该愿景方面取得的进展进行更新。

在 Rust 2024 中编写异步问题聚合器

设想一下,现在是 Rust 2024 年,你决定用 Rust 构建你的第一个项目。你正在做一个使用 GitHub 的项目,并且希望有一个工具能遍历你仓库中的所有问题并进行一些自动分类。你决定为此使用异步 Rust。你拿出 Rust 书,翻到异步 I/O 部分。在那里,它展示了异步 Rust 应用程序的基本结构。像任何 Rust 程序一样,它以 main 开始,但这次是一个 async fn...

async fn main() {
    ...
}

翻到 crates.io,你搜索“github”,发现有一个很棒的 crate crabbycat 用于导航 github 问题。你导入它并开始工作。你需要做的第一件事就是遍历所有问题

async fn main() {
    for await? issue in crabbycat::issues("https://github.com/rust-lang/rust") {
        if meets_criteria(&issue) {
            println!("{issue:?}");
        }
    }
}

你的 crate 似乎工作良好,你高兴地发推特分享。很快你就获得了一些用户,其中一人提出了一个 PR,希望扩展它以支持 GitLab。为此,他们引入了一个 trait,允许你编写对问题提供者泛化的代码。这个 trait 有一个方法 issues,它返回一个迭代器(在这种情况下,是一个异步迭代器)

trait IssueProvider {
    async fn issues(&mut self, url: &str)
        -> impl AsyncIterator<Item = Result<Issue, Err>>;
}

#[derive(Debug)]
struct Issue {
    number: usize,
    header: String,
    assignee: String,
}

现在他们可以将主循环重构为一个对 IssueProvider 泛化的函数。他们决定使用 dyn trait 来避免多次单态化。

fn process_issues(provider: &mut dyn IssueProvider) {
    for await? issue in provider.issues("https://github.com/rust-lang/rust") {
        if meets_criteria(&issue) {
            println!("{issue:?}");
        }
    }
}

你高兴地合并了 PR,一切运行顺利。后来,有人想将你的系统移植到 Chartreuse 操作系统上运行。Chartreuse 基于 actor 模型,并使用自己的自定义异步运行时——但幸运的是,你无需担心。你的所有代码都能无缝地将底层运行时实现切换到 Chartreuse 异步运行时。

与此同时,在 2022 年...

当然,现在仍然是 2022 年,我们刚才描绘的愿景尚未成为现实——至少现在还没有。在 RFC 流程和实现让我们能够编写我们讨论的代码所需的特性方面,还有很多工作要做

  • 编写 IssueProvider trait 需要在 trait 中使用 async fns。
  • 接受 &mut dyn IssueProvider 参数需要在包含 async 函数的 trait 中支持动态分发
    • 并返回 impl AsyncIterator
  • 代码使用了 for await? 循环,这允许在异步代码中轻松地遍历迭代器。
  • 标准库中用于异步迭代的 trait (Stream) 名称不同且尚未稳定;一旦我们对 trait 中的 async fns 有了强大的支持,其定义也可能会改变。
  • 编写 async fn main 并切换到备用运行时需要在不同运行时之间实现可移植性。

随着这项工作的推进,我们可以预期过程中会有许多细节变化,我们也可能会决定有些部分不值得付出努力;至少,生成器(generator)的语法是一个备受争议的话题。不会改变的是整体愿景:编写异步 Rust 代码应该像编写同步代码一样容易,除了偶尔需要使用 asyncawait 关键字。

我们如何实现它

我们已将异步工作组组织成若干不同的倡议(initiative),每个倡议都在追求愿景的一部分。以下是当前活跃的组别以及它们在启动以来过去几个月所做的一些工作清单。

异步基础倡议

tmandry 领导,目前专注于支持在 trait 中使用 async fn

异步迭代倡议

estebank 领导,探索生成器和异步生成器。

可移植性倡议

nrc 领导,探索如何使代码在不同运行时之间易于移植,首先是为 AsyncReadAsyncWrite 等标准化 trait。

完善倡议

eholk 领导,专注于通过较小的改动来改进现有功能,这些改动汇集起来会产生很大的影响。

  • 我们有一个待处理的 PR,它将在变量在 yield 点之前被移动时改进生成器的捕获分析,以及另一个 PR,它收紧临时作用域,以进一步避免不必要的生成器捕获。
  • Gus Wynnmust_not_suspend lint 方面取得了显著进展,该 lint 用于捕获不应该跨 await 点存活的值。
  • 我们开始寻找方法使异步栈跟踪更易读和更有帮助。

工具倡议

pnkfelix 领导,致力于支持异步生态系统中的开发者,他们正在构建有趣的工具来支持异步 Rust 及其他方面。

  • Michael Woerister 正在探索异步崩溃转储恢复,提供一种基于崩溃转储来恢复和检查异步 Rust 程序状态的机制。
  • Eliza Weisman 和许多其他人最近发布了 tokio console 的 0.1 版本。Tokio Console 是一款用于异步 Rust 程序的诊断和调试工具。它提供对异步运行时状态的实时视图,并在检测到可能表明 bug 或性能问题的可疑行为时发出警告。

您可以在我们的路线图页面上找到我们计划的全部工作,该页面也链接到我们正在努力实现的各种交付成果。

想帮忙?

如果您有兴趣帮忙,一个好的起点是完善倡议页面上的如何帮忙部分。还有一个每周的完善分类会议,您可能希望参加。