Rust 1.7.0的macro巨集-語法分析和使用舉例
macro 巨集的概念在很多語言中都有。
通常情況下,巨集的機制是在預編譯階段對已經定義的巨集進行替換或者 expanded 展開 ,即:把巨集按照名稱替換成巨集的內容。
Rust 中的巨集,與眾不同!
Rust中的巨集,也是在預編譯階段進行處理。巨集不僅僅是替換內容和展開,還可以像功能函式一樣,接收引數、呼叫其他的巨集。
一、簡單的巨集
巨集的名稱和功能函式名稱很像,只不過在函式名稱後面有一個歎號!
一個簡單的巨集定義和呼叫:
macro_rules! say_hello{
()=>(
println!("Hello");
)
}
fn main(){
say_hello!()
}
解釋一下:
macro_rules! 宣告一個巨集,say_hello 是巨集的名字,say_hello後面是一對大括號,是巨集的內容體。
大括號裡面的=>前面的 ( ) 表示這個巨集沒有輸入引數。
在這個巨集內部,呼叫了另外一個巨集 println!
在main()功能函式中呼叫巨集。因為這個巨集和呼叫的功能函式在一個檔案中,所以不需要額外的匯出和引入巨集的宣告。
當然,巨集的內容體除了用 {}大括號之外,還可以使用 (); 和 [];
macro_rules! say_hello ( ()=>( println!("Hello"); ) );
macro_rules! say_hello [
()=>(
println!("Hello");
)
];
⚠ 注意:
- 使用大括號的時候,大括號後面沒有分號;
- 而使用()和[]的時候,後面有一個分號。
❌如果大括號{}有分號;或者()和[]沒有分號都會編譯錯誤。
⚠ 注意:
另一個需要注意的是,巨集的輸入引數是包含在巨集內容體內的!!!
二、帶引數的巨集
下面定義一個帶引數的巨集 print_hello
macro_rules! print_hello{
($msg:expr) =>(
println!("hello{}" , $msg);
)
}
fn main(){
print_hello!(",world!");
}
上面定義了一個 print_hello 巨集,把上面的程式碼分解成 4 個部分:
第一個部分:巨集宣告
macro_rules! print_hello
- macro_rules! 是巨集定義關鍵字,注意後面有 ! 歎號
- 後面跟巨集的名稱 這是定義巨集的名稱
第二部分:巨集的內容邊界 {一對大括號}
macro_rules! print_hello {
}
第三部分:巨集的內容 -大括號中的內容
大括號中的內容被稱為 macro rule 巨集規則。一個巨集可以包含任意(0 -n)多個 macro rule 巨集規則。
($msg:expr) =>( println!("hello{}", $msg) );
($msg:expr) =>{ println!("hello{}", $msg) };
($msg:expr) =>[ println!("hello{}", $msg) ];
($msg:expr) => ( println!("hello{}", $msg); );
($msg:expr) => { println!("hello{}", $msg); };
($msg:expr) => [ println!("hello{}", $msg); ];
以上 6 句 作用完全一致!
巨集規則的格式應該是這樣的:
( ) => { };
( ) => [ ];
( ) => ( );
- 每個巨集規則有一個 => ;
- 在 => 左面的小括號是該規則的輸入引數,裡面的形式是一個 叫做 matcher 匹配器;
- 在 => 右面的括號裡是巨集的內容,需要被執行或者替換,叫做 transcriber 轉換器,可以是語句、表示式等。
以上三行程式碼等同。
matcher 和 transcriber 會在 後面的文章中詳細說明。
前面說過,巨集的引數是包含在巨集內容體內的!
對比一下功能函式的宣告格式:
fn fn_name(x:i32,y:u32){
}
功能函式的輸入引數通過 fn_name 後面的小括號傳入,引數可能是 0 個或者多個,但是所有的引數一定是在這個括號中宣告。
來看看這段程式碼:
macro_rules! foo {
(x => $e:expr) => (println!("mode X: {}", $e));
(y => $e:expr) => (println!("mode Y: {}", $e));
}
fn main() {
foo!(x => 7);
foo!(y => 3);
}
巨集的輸入不是通過引數列表傳入的!而是通過 match 的方式進行查詢或者匹配的。
當匹配到 x => 7 ,則執行 mode X: 7
當匹配到 y => 3 ,則執行 mode Y: 3
第四部分:巨集呼叫
print_hello!(",world!");
呼叫方式,使用巨集名稱後面加!歎號,歎號後面跟一個括號表示輸入引數。