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 試試一些實際的程式碼吧!
範例 - 解糖 range 運算式
以下範例也可以在專案的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 資料夾中。
隨機一篇部落格

留言
登入後參與討論