有没有一种方法可以将多个派生别名为单个派生?

时间:2019-06-04 19:58:37

标签: rust macros traits

在使用新类型模式时,我经常会得到冗长的派生:

extern crate derive_more;
use derive_more::*;

#[derive(Add, Sub, Mul, Div, ..., Copy, Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
struct Foo(i32);

是否可以将其缩短为类似这样的内容:

#[derive(Num)]
struct Foo(i32);

Num在哪里是派生宏?

我发现了this,但似乎无法扩展属性中的宏。 This answer讨论了必须如何将属性附加到项目,排除了这一点:

#[proc_macro_derive(Num)]
pub fn num_derive(_: TokenStream) -> TokenStream {
    let gen = quote! {
        #[derive(Add, Sub, Mul, Div, ..., Copy, Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
    };
    gen.into()
}

1 个答案:

答案 0 :(得分:1)

正如你所提到的,发射的属性必须附加到一个项目上,使得像这样的 proc 宏是不可能的:

#[proc_macro_derive(Num)]
pub fn num_derive(_: TokenStream) -> TokenStream {
    let gen = quote! {
        #[derive(Eq, PartialEq, Ord, PartialOrd)]
    };
    gen.into()
}

proc-macros 还带来了必须在单独的 crate 中定义的麻烦,因此生成它们或出于符合人体工程学的原因创建简单的容器是不值得的。

排除了proc-macros,我们可以看看macro_rules。它们对属性有相同的限制。但是,可以将项目定义包装在 proc 宏中并为其附加属性:

macro_rules! derive_stuff {
     ($i:item) => {
        #[derive(Eq, PartialEq, Ord, PartialOrd)]
        $i
    }
}

derive_stuff! { struct Foo(i32); }

鉴于此,我们可以创建一个宏来生成一个类似上面的宏:

macro_rules! derive_alias {
    ($name:ident => #[derive($($derive:ident),*)]) => {
        macro_rules! $name {
            ($i:item) => {
                #[derive($($derive),*)]
                $i
            }
        }
    }
}

derive_alias! {
    derive_stuff => #[derive(Eq, PartialEq, Ord, PartialOrd)]
}

derive_stuff! { struct Foo(i32); }

所以我创建了一个箱子 (derive_alias) 来做到这一点:

use derive_alias::derive_alias;
use derive_more::*;

derive_alias! {
    derive_num => #[derive(Add, Sub, Mul, Div, ..., Copy, Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
}

derive_num! { struct Foo(i32); }