2015-05-17 31 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'是您唯一的选择。 –