Expand description

::macro_rules_attribute

Use declarative macros in attribute or derive position.

macro_rules! my_fancy_decorator { /* … */ }

#[apply(my_fancy_decorator!)]
struct Foo { /* … */ }
macro_rules! MyFancyDerive { /* … */ }

#[derive(MyFancyDerive!)]
struct Foo { /* … */ }

Latest version Documentation License

Motivation

macro_rules! macros can be extremely powerful, but their call-site ergonomics are sometimes not great, especially when decorating item definitions.

Indeed, compare:

foo! {
    struct Struct {
        some_field: SomeType,
    }
}

to:

#[foo]
struct Struct {
    some_field: SomeType,
}
  1. The former does not scale well, since it leads to rightward drift and “excessive” braces.

  2. But on the other hand, the latter requires setting up a dedicated crate for the compiler, a proc-macro crate. And 99% of the time this will pull the ::syn and ::quote dependencies, which have a non-negligible compile-time overhead (the first time they are compiled).

    • note: these crates are a wonderful piece of technology, and can lead to extremely powerful macros. When the logic of the macro is so complicated that it requires a recursive tt muncher when implemented as a macro_rules! macro, it is definitely time to be using a procedural macro.

      Anything involving ident generation / derivation, for instance, will very often require procedural macros, unless it is simple enough for ::paste to handle it.


Solution

With this crate’s #[apply] and #[derive] attributes, it is now possible to use proc_macro_attribute syntax to apply a macro_rules! macro:

#[macro_use]
extern crate macro_rules_attribute;

macro_rules! foo {
    // …
}

macro_rules! Bar {
    // …
}

#[apply(foo)]
#[derive(Debug, Bar!)]
struct Struct {
    some_field: SomeType,
}

without even depending on ::quote, ::syn or ::proc-macro2, for fast compile times.

  • Note: for even faster compile times, feel free to disable the derive-alias Cargo feature, should you not use it.

    On my machine, that feature requires around 0.3s of extra compile-time, which is not much, but still a 25% increase w.r.t. --no-default-features.

Example

Deriving getters for a (non-generic) struct:

#[macro_use]
extern crate macro_rules_attribute;

macro_rules! make_getters {(
    $(#[$struct_meta:meta])*
    $struct_vis:vis
    struct $StructName:ident {
        $(
            $(#[$field_meta:meta])*
            $field_vis:vis // this visibility will be applied to the getters instead
            $field_name:ident : $field_ty:ty
        ),* $(,)?
    }
) => (
    // First, generate the struct definition we have been given, but with
    // private fields instead.
    $(#[$struct_meta])*
    $struct_vis
    struct $StructName {
        $(
            $(#[$field_meta])*
            // notice the lack of visibility => private fields
            $field_name: $field_ty,
        )*
    }

    // Then, implement the getters:
    impl $StructName {
        $(
            #[inline]
            $field_vis
            fn $field_name (self: &'_ Self)
                -> &'_ $field_ty
            {
                &self.$field_name
            }
        )*
    }
)}

mod example {
    #[apply(make_getters)]
    /// The macro handles meta attributes such as docstrings
    pub
    struct Person {
        pub
        name: String,

        pub
        age: u8,
    }
}
use example::Person;

fn is_new_born (person: &'_ Person)
  -> bool
{
    // person.age == 0
    // ^ error[E0616]: field `age` of struct `example::Person` is private
    *person.age() == 0
}

Debugging

An optional compilation feature, "verbose-expansions" can be used to print at compile-time the exact output of each macro invocation from this crate:

[dependencies]
macro_rules_attribute.version = "..."
macro_rules_attribute.features = ["verbose-expansions"]

Bonus tricks

derive aliases

#[macro_use]
extern crate macro_rules_attribute;

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

#[derive(Debug, Clone, Copy, Ord!)]
struct Foo {
    // …
}

cfg aliases

#[macro_use]
extern crate macro_rules_attribute;

attribute_alias! {
    #[apply(complex_cfg!)] = #[cfg(
        any(
            any(
                foo,
                feature = "bar",
            ),
            all(
                target_os = "fenestrations",
                not(target_arch = "Pear"),
            ),
        ),
    )];
}

#[apply(complex_cfg!)]
mod some_item { /* … */ }

Macros

Convenience macro to define new attribute aliases.

Convenience macro to define new derive aliases.

Attribute Macros

Applies the given macro_rules! macro to the decorated item.

Like #[macro_rules_derive], but for allowing to be used to shadow the “built-in” #[derive] attribute.

Legacy name for what is currently named #[apply]

Applies the given macro_rules! macro to the decorated item.