有没有办法将多个派生别名为单个派生?

Is there a way to alias multiple derives as a single one?

在使用 newtype 模式时,我经常有冗长的派生:

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, but it seems like one can't expand macros in attributes. 讨论了如何将属性附加到项目,排除了这一点:

#[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()
}

正如您所提到的,发出的属性必须附加到一个项目,使得这样的 proc-macro 不可能:

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

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

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

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); }