定义特征别名的宏
Macro for defining trait aliases
根据这个问题 issue and this answered 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
不是表达式。我尝试了其他几种变体,但没有成功。是否可以定义这样的宏?它应该是什么样子?
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);
但是请注意,它会将语法检查转移到宏内部,这不是最佳选择。
这个宏的想法非常有用,trait-set crate 实现了它。此时无需重新发明轮子 - 板条箱运行良好。
这是我用它写的一个例子:
trait_set! {
pub trait Hashable = Hash + Eq + Clone;
}
根据这个问题 issue and this answered 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
不是表达式。我尝试了其他几种变体,但没有成功。是否可以定义这样的宏?它应该是什么样子?
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);
但是请注意,它会将语法检查转移到宏内部,这不是最佳选择。
这个宏的想法非常有用,trait-set crate 实现了它。此时无需重新发明轮子 - 板条箱运行良好。
这是我用它写的一个例子:
trait_set! {
pub trait Hashable = Hash + Eq + Clone;
}