Macro dmutil::eager
[−]
[src]
macro_rules! eager { ( $($all:tt)* ) => { ... }; }
Emulates eager expansion of macros.
Example
#[macro_use] extern crate dmutil; eager_macro_rules!{ plus_1 $eager_1 $eager_2 ()=>{ + 1 }; } fn main(){ assert_eq!(4, eager!{2 plus_1!() plus_1!()}); }
Usage
eager!
can wrap any code, and if that code contains a macro call, that macro will be
expanded before its consumer. This means:
- If a macro call is given as an argument to another macro, the first macro will be expanded first.
- All macros will be fully expanded before
eager!
expands, meaning otherwise illegal intermediate expansion steps can be made possible.
eager!
does not work with any macro. Only macros declared using eager_macro_rules! may be
used inside eager!
. Such macros are said to be eager!
-enabled.
Cons
-
Because of the way
eager!
is implemented; being a hack of recursive macroes, the compiler's default macro recursion limit is quickly exceeded. Therefore,#![recursion_limit="256"]
must be used in most situations (potentially with a higher limit) such that expansion can happen. -
Debugging an eagerly expanded macro is very difficult and requires intimate knowledge of the implementation of
eager!
. There is no way to mitigate this, except to try and recreate the bug without usingeager!
. Likewise, the error messages the compiler will emit are exponentially more cryptic than they already would have been. -
Only works with
eager!
-enabled macros. Additionally, none of the macros may expand to something containing a non-eager!
-enabled macro, not even as an intermediate expansion.
Macro expansions
Rust is lazy when it comes to macro expansion. When the compiler sees a macro call, it will
try to expand the macro without looking at its arguments or what the expansion becomes.
Using eager!
, previously illegal macro expansions can be made possible.
Macro restrictions
This puts a few restrictions on what can and cannot be done with macros:
The arguments to a macro usually cannot be the resulting expansion of another macro call:
Say you have a macro that adds two numbers:
macro_rules! add{ ($e1:expr, $e2:expr)=> {$e1 + $e2} }
And a macro that expands to two comma-separated numbers:
macro_rules! two{ ()=>{2,3} }
You cannot use the expansion of two!
as an argument to add!
:
let x = add!(two!()); // error
The compiler will complain about no rule in add!
accepting two
, since two!()
does not
get expanded before the add!
who requires two expression and not just one.
With eager expansion, this can be made possible:
#[macro_use] extern crate dmutil; eager_macro_rules!{ add $eager_1 $eager_2 ($e1:expr, $e2:expr)=> {$e1 + $e2} } eager_macro_rules!{ two $eager_1 $eager_2 ()=>{2,3} } fn main(){ let x = eager!{add!(two!())}; assert_eq!(5, x); }
An intermediate expansion step cannot result in invalid syntax
Say you have a macro that expands to an identifier:
macro_rules! id{ ()=> {SomeStruct} }
And want a macro that, using the previous macro, expands to a struct declaration:
macro_rules! some_struct{ ()=> {struct id!(){}} }
Calling this macro will not compile. Looking at the result of the first expansion step we can see why:
struct id!() {}
Since macro calls are illegal in identifier positions, the compiler will refuse to continue expanding.
With eager expansion, this can be made possible:
#[macro_use] extern crate dmutil; eager_macro_rules!{ id $eager_1 $eager_2 ()=> {SomeStruct} } eager_macro_rules!{ some_struct $eager_1 $eager_2 ()=>{struct id!(){}} } eager!{some_struct!{}} fn main(){}
Trivia
Ironically, eager!
is not itself eager!
-enabled,
though it does ignore itself if it is nested.