::macro_rules_attribute
Use declarative macros in attribute or derive position.
```rust ,ignore macrorules! myfancy_decorator { /* … */ }
struct Foo { /* … */ } ```
```rust ,ignore macro_rules! MyFancyDerive { /* … */ }
struct Foo { /* … */ } ```
Click to see
macro_rules!
macros can be extremely powerful, but their call-site ergonomics
are sometimes not great, especially when decorating item definitions.
Indeed, compare:
rust ,ignore
foo! {
struct Struct {
some_field: SomeType,
}
}
to:
```rust ,ignore
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 a
macro_rules!
macro, it is definitely time to be using a proc
edural
macro.
Anything involving ident
generation / derivation, for instance, will very
often require proc
edural macros, unless it is simple enough for
[::paste
] to handle it.
With this crate's #[[apply]]
and #[[derive]]
attributes, it is now possible to use proc_macro_attribute
syntax to apply a
macro_rules!
macro:
```rust
extern crate macrorulesattribute;
macro_rules! foo { // … # ( $($tt:tt)* ) => () }
macro_rules! Bar { // … # ( $($tt:tt)* ) => () }
struct Struct { some_field: SomeType, } #
```
without even depending on [::quote
], [::syn
] or [::proc-macro2
], for
fast compile times.
Click to see
```rust
extern crate macrorulesattribute;
// Easily define shorthand aliases for "derive groups" derive_alias! { #[derive(Eq!)] = #[derive(Eq, PartialEq)]; #[derive(Ord!)] = #[derive(Ord, PartialOrd, Eq!)]; #[derive(Copy!)] = #[derive(Copy, Clone)]; #[derive(StdDerives!)] = #[derive(Debug, Copy!, Default, Ord!, Hash)]; }
/// Strongly-typed newtype wrapper around a usize
, to be used for PlayerId
s.
pub struct PlayerId /* = */ ( pub usize, );
// You can also fully define your own derives using macro_rules!
syntax
// (handling generic type definitions may be the only finicky thing, though…)
macrorules! Into {(
$( #[$attr:meta] )*
$pub:vis
struct $NewType:ident (
$(#[$fieldattr:meta])*
$field_pub:vis
$Inner:ty $(,
$($rest:tt)* )?
);
) => ( impl ::core::convert::Into<$Inner> for $NewType { #[inline] fn into (self: $NewType) -> $Inner { self.0 } } )} use Into;
macrorules! From {( $( #[$attr:meta] )* $pub:vis struct $NewType:ident ( $(#[$fieldattr:meta])* $field_pub:vis $Inner:ty $(,
$(#[$other_field_attr:meta])*
$other_field_pub:vis
$Rest:ty )* $(,)?
);
) => ( impl ::core::convert::From<$Inner> for $NewType { #[inline] fn from (inner: $Inner) -> Self { Self(inner, $($Rest::default),*) } } )} use From; #
```
-lite
version of a proc-macro dependency that thus requires unergonomic macro_rules!
?Say you are writing a (pervasive and yet) tiny dependency within the async
ecosystem.
By virtue of working with async
, you'll most probably need to deal with
pin-projections, and thence, with [::pin-project
].
But by virtue of being (pervasive and yet) tiny, you don't want to depend
on the quote / proc-macro2 / syn
heavyweight[^onlyfullsynisheavy]
troika/trinity/triumvirate of more advanced proc-macro crates.
Hence why you may reach for something such as [::pin-project-lite
], and its
pin_project!
macro_rules!
-based polyfill of the former's #[pin_project]
attribute.
But this suddenly hinders the ergonomics of your type definitions, and, worse,
would not be composable whenever the pattern were to be repeated for some other
functionality (e.g., say a cell_project!
similar macro).
Say no more! Time to #[[apply]]
our neat trick:
```rust
extern crate macrorulesattribute;
use { ::core::pin::{ Pin, }, ::pinprojectlite::{ pin_project, }, };
struct Struct
impl
```
lazy_static!
sSay you had something like:
```rust
#
static MY_GLOBAL: &dyn Logic = &Vec::
and now you want to change the value of that MY_GLOBAL
to something that isn't
const
-constructible, and yet would like to minimize the churn in doing so.
rust ,compile_fail
// (For those unaware of it, leaking memory to initialize a lazy static is
// a completely fine pattern, since it only occurs once, and thus, a bounded
// amount of times).
static MY_GLOBAL: &dyn Logic = Box::leak(Box::new(vec![42, 27])); // Error: not `const`!
You could directly use a lazy_static!
or a OnceCell
, but then the
definition of your static
will now appear noisier than it needs be. It's time
for attribute-position polish!
First, define the helper around, say, OnceCell
's Lazy
type:
rust
macro_rules! lazy_init {(
$( #[$attrs:meta] )*
$pub:vis
static $NAME:ident: $Ty:ty = $init_value:expr ;
) => (
$( #[$attrs] )*
$pub
static $NAME : ::once_cell::sync::Lazy<$Ty> =
::once_cell::sync::Lazy::new(|| $init_value)
;
)} pub(in crate) use lazy_init;
and now it is time to use it!:
```rust
#
extern crate macrorulesattribute;
static MY_GLOBAL: &dyn Logic = Box::leak(Box::new(vec![42, 27])); #
#
```
An optional compilation feature, "verbose-expansions"
can be used to print at
compile-time the exact output of each macro invocation from this crate:
toml
[dependencies]
macro_rules_attribute.version = "..."
macro_rules_attribute.features = ["verbose-expansions"]
derive
aliases```rust
extern crate macrorulesattribute;
derive_alias! { #[derive(Ord!)] = #[derive(PartialEq, Eq, PartialOrd, Ord)]; }
struct Foo { // … } ```
derive_alias!
] and #[[derive]]
for more info.cfg
aliasesClick to see
```rust
extern crate macrorulesattribute;
attributealias! { #[apply(complexcfg!)] = #[cfg( any( any( foo, feature = "bar", ), all( targetos = "fenestrations", not(targetarch = "Pear"), ), ), )]; }
mod some_item { /* … */ } ```
#[macro_use] extern crate macro_rules_attribute
Click to see
If you are allergic to #[macro_use]
unscoped / globally-preluded semantics,
you may not be fond of having to use:
```rust
extern crate macrorulesattribute;
```
like this documentation pervasively does.
In that case, know that you may very well stick to using use
imports:
```rust use ::macrorulesattribute::{derive, derivealias, /* … */}; // or even use ::macrorules_attribute::*;
derive_alias! { #[derive(Copy!)] = #[derive(Clone, Copy)]; }
struct Foo; ```
or even inlining the fully qualified paths (but note that the …_alias!
macros
still take unqualified paths inside the definitions):
```rust ::macrorulesattribute::derive_alias! { #[derive(Copy!)] = #[derive(Clone, Copy)]; }
struct Foo; ```
I personally find these approaches too noisy to be worth it, despite the so gained "namespace purity", hence my not using that pattern across the rest of the examples.