Rust 在幕后做了什么?
原文由 Matthias Endler 于 发布,订阅该博客
Rust 提供了大量的语法糖,让编写代码成为一种享受。然而,有时却很难透过幕布,看清编译器究竟对我们的代码做了什么。
在 Rust Belt Rust 2018 上,我观看了 Tshepang Lekhonkhobe 题为 Syntax conveniences afforded by the compiler 的演讲(在此观看录像)。
引用其摘要如下:
Rust 编译器提供了许多便利,让用户使用起来更加轻松。了解这些便利是什么很有好处,这样才不会对底层发生的事情感到困惑……我们对世界的魔法式想象越少越好。
他接着举了几个这类便利的例子:
- 生命周期省略
- 类型推导
- 语法糖
- 隐式解引用
- 类型强制转换
- 隐藏代码(例如 prelude)
在演讲中,看他对比使用和不使用这些便利时的代码,既有启发又很有趣。
回家后,我想进一步了解。我在想,是否有工具能揭示 Rust 在幕后究竟做了什么。
在 Reddit 上,我找到了一篇关于通过编译器参数生成去糖后输出的讨论。(注意,这里我使用 rustup 并通过 +nightly 参数来调用 nightly 版编译器。)
rustc +nightly -Zunpretty=hir example.rsHIR 是 high-level intermediate representation(高层中间表示)的缩写。它本质上是一种更适合编译器使用的抽象语法树(AST),会将语法糖替换为更基础的构造单元,以便后续的编译步骤更容易处理。想了解更多,可以阅读Nico Matsakis 的这篇详细介绍。
不管怎样,输出结果的可读性出人意料地好(见下文)。如果加上语法高亮和格式化,它会是一个相当趁手的工具。
我试着用 rustfmt 来处理它,效果好得出奇。受到这次小成功的鼓舞,我把它封装成了一个 cargo 子命令,并命名为 cargo-inspect。
让我们在真实代码上试试 cargo-inspect 吧!
示例——对区间表达式去糖
下面的例子也可以在项目的 examples 文件夹中找到。
输入:
for n in 1..3 {
println!("{}", n);
}cargo-inspect 的输出:
这就是经过整洁格式化的终端输出。得益于 prettyprint(一个基于 bat 的库),它带有行号和颜色。也许你看不清,所以这里是它的核心内容:
match ::std::iter::IntoIterator::into_iter(
::std::ops::Range { start: 1, end: 3 })
mut iter => loop {
// ...
},
};可以看到,1..3 被转换成了 std::ops::Range { start: 1, end: 3 }。对于编译器后端来说,二者完全等价。因此下面这行断言是成立的:
assert_eq!((1..3), std::ops::Range { start: 1, end: 3 });示例——文件处理
输入:
use std::fs::File;
use std::io::Error;
fn main() -> Result<(), Error> {
let file = File::open("file.txt")?;
Ok(())
}输出:
#[prelude_import]
use std::prelude::v1::*;
#[macro_use]
extern crate std;
use std::fs::File;
use std::io::Error;
fn main() -> Result<(), Error> {
let file = match ::std::ops::Try::into_result(
<File>::open("file.txt")) {
::std::result::Result::Err(err) =>
#[allow(unreachable_code)]
{
#[allow(unreachable_code)]
return ::std::ops::Try::from_error(
::std::convert::From::from(err))
}
::std::result::Result::Ok(val) =>
#[allow(unreachable_code)]
{
#[allow(unreachable_code)]
val
}
};
Ok(())
}可以看到,? 运算符被去糖为对 File::open 返回的 Result 进行的 match 匹配。如果出现错误,就会调用 std::convert::From::from 在错误类型之间进行转换。否则,就直接返回 Ok 中的值。
演讲
在比利时的 FOSDEM 上,我有机会详细介绍了这个项目。以下是演讲录像:
未来工作
我并不打算在这里重写编译器。rustc 做得比我能做到的要好得多。所有这些功能本来就已经存在;我只是想让编译器对像我这样的学习者更友好一些。
目前,这个工具还相当脆弱。出错时会抛出难看的错误信息。它主要在处理小而独立的示例片段时表现最好。
欢迎参与!
在 GitHub 上,我开了几个 issue,欢迎大家参与。具体来说,我希望能实现以下功能:
另外,如果你发现了特别有意思的代码示例,也欢迎大方地贡献到 examples 文件夹中。
随机一篇博客

评论
登录后参与讨论