如果您對宏不熟悉,那麼您對 Rust 也不熟悉。
在本指南中,我們將揭秘支撐數十萬個 Rust 應用程式的三個宏。它們不是由巫師編寫的,而是由我們渴望成為的才華橫溢的 Rust 工程師編寫的。
這三個宏都是 "宣告式 "的,也就是用宏規則(macro_rules! 這些宏構成了 Rust 宏的主體,將標記作為輸入,進行模式匹配,並根據匹配結果輸出原始碼。 程式宏構成了宏家族的其他部分,涉及對標記流的程式設計操作。
一個簡單的 Rust 宏:assert_eq!
let a = 3; |
assert_eq!支援兩種引數模式。
- 第一種匹配兩個表示式,a, b並比較它們的值是否相等。
- 第二種也接受格式字串和任意數量的引數進行插值。
如果任一匹配模式的相等性檢查失敗,assert_eq!則會產生panic。
因為我們有兩種引數模式,所以你正確地期望宏定義中有兩條規則:
#[macro_export] |
- 第一個宏:採用兩個表示式 - 等式的左邊和右邊 - 以及可選的尾隨逗號。
- 第二個宏:規則 2 匹配同樣的兩個表示式,但也匹配與格式字串及其引數相對應的一個或多個TokenTree 令牌樹。
TokenTree :可以是單個標記(如 3 或 "hello"),也可以是由()、[] 或 {} 包含的任意數量的巢狀標記。
它們的擴充套件幾乎完全相同:
#[macro_export] |
這兩條規則產生的程式碼都會對左手和右手錶達式進行求值,並將輸出結果的引用儲存在 left_val 和 right_val 3 中。 這樣,我們就不必多次求值 $left 或 $right。
請記住,雖然 assert_eq! 例子使用了原始型別,但 $left 和 $right 可能是昂貴的函式呼叫。
接下來,使用 PartialEq 4 進行簡單的相等性檢查。 當檢查失敗時,事情就變得有趣了。
兩種規則都將 $crate::panicking::AssertKind::Eq 賦值給變數 kind 5 。
Rust 很聰明,這個宏本地的 kind 變數不會與任何呼叫 assert_eq! 的名為 kind 的變數發生衝突,這就是所謂的宏衛生(macro hygiene)特性。
但為什麼要使用完全限定的模組路徑來指定 AssertKind::Eq 呢? 當你使用 #[macro_export] 匯出宏時,並不能保證在使用該宏時的作用域中會出現什麼。
assert_eq! 的使用者不應該被要求自己匯入這個實現細節,而完全限定的路徑可以避免這一點。
...
更多點選標題