用于定义特征别名的宏

Sim*_*mon 6 macros rust

根据这个问题和这个回答的问题,不可能简单地定义一个特征别名,如:

trait Alias = Foo + Bar;
Run Code Online (Sandbox Code Playgroud)

解决方法有点难看:

trait Alias : Foo + Bar {}
impl<T: Foo + Bar> Alias for T {}
Run Code Online (Sandbox Code Playgroud)

因此,我想为此定义一个宏.我试过了

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);
Run Code Online (Sandbox Code Playgroud)

但它失败了,错误:

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

可能Foo + Bar不是表达.我尝试了其他几种变化,但没有运气.是否可以定义这样的宏?应该怎么样?

Chr*_*gan 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 +);
Run Code Online (Sandbox Code Playgroud)

(由于技术原因,你不能有更好的$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);
Run Code Online (Sandbox Code Playgroud)

但请注意,它会在宏内部转换语法检查,这不是最佳选择.