2015-06-02 45 views
10

我有一個宏創建一個結構和一堆支持函數和特徵實現。對於這個問題,我們感興趣的是:如何從宏創建參數化類型?

macro_rules! make_struct { 
    ($name: ident) => { 
     struct $name; 
    } 
} 

這工作你會期望:

make_struct!(MyStruct); 

如果我想打一個參數化類型然而,我的運氣:

make_struct!(AnotherStruct<T: SomeTrait>); 

test.rs:8:27: 8:28 error: no rules expected the token `<` 
test.rs:8 make_struct!(AnotherStruct<T: SomeTrait>); 

該結構的名稱是ident所以我不能只是改變,在宏觀ARGS(如到ty):

test.rs:3:16: 3:21 error: expected ident, found `MyStruct` 
test.rs:3   struct $name; 

那麼我該如何編寫這個宏來處理兩者?或者我需要分開嗎?在後一種情況下,宏是什麼樣的?

回答

4

在關鍵字struct之後,解析器需要一個ident令牌樹,其後面可以跟着<以及更多; ty絕對不是它想要的(作爲爲什麼它不起作用的一個例子,(Trait + Send + 'static)是一個有效的ty,但struct (Trait + Send + 'static);顯然沒有意義)。

要支持泛型,您需要制定更多規則。

macro_rules! make_struct { 
    ($name:ident) => { 
     struct $name; 
    }; 
    ($name:ident<$($t:ident: $constraint:ident),+>) => { 
     struct $name<$($t: $constraint),+>; 
    } 
} 

您必定觀察,使得它可以支持任何解析器將在該位置接受的是,幾乎是不可能的; macro_rules不是那麼聰明。然而,這是一個奇怪的技巧(靜態代碼分析器討厭它!),它允許您採取一系列令牌樹並將其視爲正常的struct定義。它使用了更多的只是一點點間接與macro_rules:

macro_rules! item { 
    ($item:item) => ($item); 
} 
macro_rules! make_struct { 
    ($name:ident) => { 
     struct $name; 
    }; 
    ($name:ident<$($tt:tt)*) => { 
     item!(struct $name<$($tt)*;); 
    }; 
} 

注意的是,由於過份熱衷,ident目前不允許重複序列($(…))立即跟隨它,所以你會堅持把一些令牌在ident和重複之間的樹,例如(產生AnotherStruct, <T: SomeTrait>)或$name:ident<$(tt:tt)* =>struct $name<$($tt)*;。由於你不能很容易地將它拉開以獲得單獨的泛型類型,所以你需要爲插入PhantomData標記等事情做些事情。

您可能會發現它有助於通過整個struct項目;它通過item類型(就像fn,enum,use,trait,& c。會做的那樣)。

相關問題