iPotato

iPotato

马上订阅 iPotato RSS 更新: https://ipotato.me/feed

Rust 的 async/await 语法是怎样工作的

2022年1月24日 15:11

从最开始的宏到现在的 Rust 关键字,距离 async/await 语法的 rfc 被提出已经过去将近 4 年了。相比于回调地狱,或者类似 CPS-Style 的铁索连环套娃(此处应有圣经传唱:一个 Monad 说白了不过就是自函子范畴上的一个幺半群而已),async/await 的存在无疑提供了一种良好的异步代码编写方式,它更像是把同步代码写法的异步化,让代码编写者能够最大限度的遵循同步代码编写方式,但同时提供异步的运行时表现。

不过,有言道:”哪有什么岁月静好,不过是有人替你负重前行“。想要代码写的爽,编译器一定会在背后做很多”脏活累活“。Rust 的 async/await 语法具体是怎样工作的?它又是如何将我们写的代码,转化成异步执行的呢?

先来看一段代码。

#[inline(never)]
async fn x() -> usize {
    5
}

再简单不过的一个 async 函数,只会返回一个 5,为了防止被编译器优化掉,我们给它加上了一个 #[inline(never)] 属性。这个异步函数的等价同步代码长这样:

#[inline(never)]
fn x() -> impl Future<Output = usize> {
    async { 5 }
}

async fn 其实就是会返回一个 Future trait 的函数。不过这一步转化并没有帮助我们更深地理解 async 关键字到底做了什么。为了一探究竟,我们可以尝试看看上述代码的 HIR 长什么样。HIR 是 Rust 在编译过程中的一个中间产物,在转化成更为晦涩难懂的 MIR 之前,它可以帮助我们一窥编译器的小小细节。

cargo rustc -- -Z unpretty=hir

输出如下(为了方便展示,我做了一些格式上的调整):

#[inline(never)]
async fn x()
 -> /*impl Trait*/ #[lang = "from_generator...

剩余内容已隐藏

查看完整文章以阅读更多