Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
2.3k views
in Technique[技术] by (71.8m points)

rust - Macro for defining trait aliases

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:

srcmain.rs:5:17: 5:22 error: expected one of `?`, `where`, or `{`, found `Foo + Bar`
srcmain.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?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...