According to this isuue issue and this answered question it is not possible to simply define a trait alias like:
trait Alias = Foo + Bar;
The workaround is a bit ugly:
trait Alias : Foo + Bar {}
impl<T: Foo + Bar> Alias for T {}
Therefore I want to define a macro for this. I tried
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);
But it fails with error:
src\main.rs:5:17: 5:22 error: expected one of `?`, `where`, or `{`, found `Foo + Bar`
src\main.rs:5 trait $name : $base {}
^~~~~
Probably Foo + Bar
is not an expression. I tried several other variations but with no luck. Is it possible to define such a macro? How should it look like?
expr
is an expression token tree, which clearly doesn’t fit in the locations you have tried to place it. Remember that Rust macros are strongly typed: only the types of token trees expected at a given location are permitted.
You’ll need to use sequence repetition ($(…)*
et al.) of ident
to achieve this:
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 +);
(You can’t have the nicer $base1:ident $(+ $base2:ident)+
or $($base:ident)++
at present for technical reasons.)
There is, however, a technique for cheating, making the macro parser accept things that it would not otherwise: passing them through another macro and forcing it to reinterpret the token trees as a different type. This can be used to good effect here:
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);
Note, however, that it will shift syntax checking inside the macro, which is less optimal.