::macro_rules_attribute
Use declarative macros as proc_macro attributes or derives.
Motivation
macro_rules!
macros can be extremely powerful, but their call-site ergonomics
are sometimes not great, especially when decorating item definitions.
Indeed, compare:
-
# #[cfg(any())] foo! { struct Struct { some_field: SomeType, } }
-
# #[cfg(any())] #[foo] struct Struct { some_field: SomeType, }
-
The former does not scale well, since it leads to rightward drift and "excessive" braces.
-
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 amacro_rules!
macro, it is definitely time to be using aproc
edural macro.Anything involving
ident
generation / derivation, for instance, will very often requireproc
edural macros, unless it is simple enough for::paste
to handle it.
-
Solution
With the macro_rules_attribute
and macro_rules_derive
attributes, it is
now possible to use proc_macro_attribute
syntax to apply a macro_rules!
macro:
# use ::macro_rules_attribute::macro_rules_attribute;
# macro_rules! foo {($($tt:tt)*) => ()}
#
#[macro_rules_attribute(foo!)]
struct Struct {
some_field: SomeType,
}
without even depending on ::quote
, ::syn
or ::proc-macro2
, for
fast compile times.
Example
Deriving getters for a (non-generic) struct
:
# macro_rules! ignore {($($tt:tt)*) => () }
# ignore! {
#[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 {
# use ::macro_rules_attribute::macro_rules_attribute;
#[macro_rules_attribute(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 macro call:
[dependencies]
macro_rules_attribute = { version = "...", features = ["verbose-expansions"] }
#[apply(macro!)]
shorthand
The Just a convenient shorthand for #[macro_rules_attribute(macro!)]
:
Example
#[macro_use]
extern crate macro_rules_attribute;
macro_rules! complex_cfg {( $item:item ) => (
#[cfg(any(
any(
foo,
feature = "bar",
),
all(
target_os = "fenestrations",
not(target_arch = "Pear"),
),
))]
$item
)}
#[apply(complex_cfg!)]
mod some_item { /* … */ }