Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

When using the newtype pattern I often have lengthy derives:

extern crate derive_more;
use derive_more::*;

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

Is there a way to shorten this to something like this:

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

Where Num is a derive macro?

I found this, but it seems like one can't expand macros in attributes. This answer discusses how attributes must be attached to items, ruling this out:

#[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()
}
like image 383
HiDefender Avatar asked Jun 04 '19 19:06

HiDefender


1 Answers

As you mentioned, emitted attributes must be attached to an item, making a proc-macro like this impossible:

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

proc-macros also come with the hassle of having to be defined in a separate crate, so generating them, or creating simple ones for ergonomic reasons is not worth it.

With proc-macros ruled out, we can look to macro_rules. They have the same restriction regarding attributes. However, it is possible to wrap an item definition in a proc-macro and attach attributes to it:

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

derive_stuff! { struct Foo(i32); }

Given this, we can create a macro that generates a macro like above:

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

So I created a crate (derive_alias) that does exactly that:

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); }
like image 194
Ibraheem Ahmed Avatar answered Oct 22 '22 21:10

Ibraheem Ahmed