2015-05-17 34 views
5

根據該isuue issueanswered question,不可能簡單地定義諸如性狀別名:宏定義性狀別名

trait Alias = Foo + Bar; 

解決方法是有點難看:

trait Alias : Foo + Bar {} 
impl<T: Foo + Bar> Alias for T {} 

因此我想爲此定義一個宏。我試圖

macro_rules! trait_alias { 
    ($name : ident, $base : expr) => { 
     trait $name : $base {} 
     impl<T: $base> $name for T {} 
    }; 
} 

trait Foo {} 
trait Bar {} 

trait_alias!(Alias, Foo + Bar); 

但它失敗,錯誤:

src\main.rs:5:17: 5:22 error: expected one of `?`, `where`, or `{`, found `Foo + Bar` 
src\main.rs:5  trait $name : $base {} 
            ^~~~~ 

大概Foo + Bar不是一個表達式。我嘗試了幾個其他的變化,但沒有運氣。有沒有可能定義這樣一個宏?它應該是什麼樣子?

回答

8

expr是一個表達式標記樹,顯然不適合您試圖放置它的位置。請記住,Rust宏是強類型的:只允許在給定位置預期的令牌樹類型。

你需要使用重複序列($(…)*)的ident來實現這一目標:

macro_rules! trait_alias { 
    ($name:ident = $base1:ident + $($base2:ident +)+) => { 
     trait $name: $base1 $(+ $base2)+ { } 
     impl<T: $base1 $(+ $base2)+> $name for T { } 
    }; 
} 

trait Foo { } 
trait Bar { } 

trait_alias!(Alias = Foo + Bar +); 

(你不能有目前更好$base1:ident $(+ $base2:ident)+$($base:ident)++出於技術原因。 )

然而,存在一種作弊技術,使得宏解析器接受它不會以其他方式進行的事情:將它們傳遞給另一個宏並強制它將令牌樹重新解釋爲另一種類型。這可以用來效果好這裏:

macro_rules! items { 
    ($($item:item)*) => ($($item)*); 
} 

macro_rules! trait_alias { 
    ($name:ident = $($base:tt)+) => { 
     items! { 
      trait $name: $($base)+ { } 
      impl<T: $($base)+> $name for T { } 
     } 
    }; 
} 

trait Foo {} 
trait Bar {} 

trait_alias!(Alias = Foo + Bar); 

但是請注意,它會轉移語法宏,這是次優的內部檢查。

+0

是不是'ident'限制太多(在第一個例子中)?它不會允許像'other_module :: Foo'這樣的東西。我想它應該是'路徑'。 –

+0

@VladimirMatveev:特質邊界位置不喜歡'路徑'。雖然沒有使用'items'解決方法,但使用'ident'是您唯一的選擇。 –