1. 程式人生 > >Rust 1.7.0的macro巨集-語法分析和使用舉例

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 句 作用完全一致!

巨集規則的格式應該是這樣的:

( ) => {  };
( ) => [  ];
( ) => (  );
  1. 每個巨集規則有一個 => ;
  2. 在 => 左面的小括號是該規則的輸入引數,裡面的形式是一個 叫做 matcher 匹配器;
  3. 在 => 右面的括號裡是巨集的內容,需要被執行或者替換,叫做 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!");

呼叫方式,使用巨集名稱後面加!歎號,歎號後面跟一個括號表示輸入引數。