eager!() { /* proc-macro */ }Expand description
[eager!] Emulates eager expansion of macros.
§Examples
use eager2::{eager, eager_macro};
//Declare an eager macro
#[eager_macro]
macro_rules! plus_1{
()=>{+ 1};
}
// Use the macro inside an eager! call to expand it eagerly
assert_eq!(4, eager! {2 plus_1!() plus_1!()});§Usage
eager! can wrap any code. If that code contains a macro call, it will be expanded before its
consumer. This means:
- If a macro call
b!{..}is given as an argument to another macroa!(i.e.a!(b!{...})), the inner macro (b!) will be expanded first. - All macros will be fully expanded before
eager!expands. Therefore, otherwise illegal intermediate expansion steps are possible.
eager! does not work with any macro; only macros declared using
#[eager_macro] or eager_macro_rules! may be used. Such macros are
said to be eager!-enabled.
To facilitate the use of non-eager!-enabled macros inside an eager! call,
a lazy! block can be inserted. Everything inside the lazy! block will be lazily expanded,
while everything outside it will continue to be eagerly expanded. Since, lazy! reverts
to the usual rules for macro expansion, an eager! block can be inserted inside the lazy!
block, to re-enable eager expansion for some subset of it.
§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.
The following is a non-exhaustive list of illegal macro patterns that can be used with eager!.
§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_and_three{
()=>{2,3}
}
// You cannot use the expansion of `two_and_three!` as an argument to `add!`:
let x = add!(two_and_three!()); // errorThe compiler will complain about no rule in add! accepting two_and_three, since it does not
get expanded before the add!, who requires two expressions and not just one.
With eager expansion, this can be made possible:
use eager2::{eager, eager_macro};
#[eager_macro]
macro_rules! add {
($e1:expr, $e2:expr) => { $e1 + $e2 };
}
#[eager_macro]
macro_rules! two_and_three{
()=>{2,3}
}
let x = eager! {add!(two_and_three!())};
assert_eq!(5, x);This is even possible if add cannot be made eager (e.g. it is defined in a dependency) using
lazy!:
extern crate dependency {
#[macro_export]
macro_rules! add {
($e1:expr, $e2:expr)=> {$e1 + $e2}
}
}
use eager2::{eager_macro, lazy};
#[eager_macro]
macro_rules! two_and_three {
()=>{2,3}
}
let x = lazy!{dependency::add!(eager!(two_and_three!()))};
assert_eq!(5, x);§Macros are illegal in some contexts (e.g. as an identifier)
// Say you have a macro that expands to an identifier:
macro_rules! id{
()=> {SomeStruct}
}
// And want to use it to declare a struct:
struct id!(){
v: u32
}This will not compile since macros are illegal in identifier position. The compiler does not check whether the expansion of the macro will result in valid Rust code.
With eager expansion, id! will expand before the eager! block , making it possible to use it
in an identifier position:
use eager2::{eager, eager_macro};
#[eager_macro]
macro_rules! id {
() => { SomeStruct };
}
eager! {
struct id!(){
v: u32
}
}
let some_struct = SomeStruct { v: 4 };
assert_eq!(4, some_struct.v);To circumvent any restriction on where macros can be used, we can therefore just wrap
the code surrounding the macro call with eager!. The eager! must still be in a valid position,
but in the worst case it can be put around the whole item
(struct, trait, implement, function, etc.).
§No intermediate expansion step can include invalid syntax
Say we want to create a macro that interprets natural language, converting it into an expression.
// We start by declaring a macro that interprets operator words:
macro_rules! op{
( plus ) => { + };
( minus ) => { - };
}
// We then declare a macro that interprets integer words:
macro_rules! integer{
( one ) => { 1 };
( two ) => { 2 };
}
// Lastly, we declare the top-level macro that uses the previous two macros to
// expand into an expression:
macro_rules! calculate{
( $lhs:tt $op:tt $rhs:tt ) => {
integer!{$lhs} op!{$op} integer!{$rhs}
};
}
// Using this macro will fail to compile:
let x = calculate!(one plus two); //ErrorLooking at the first expansion step we can see that three macro calls in a sequence are not a valid expression:
let x = integer!(one) op!{plus} integer!(two); //ErrorWe can circumvent this restriction, by having calculate! wrap its output in an eager!:
use eager2::{eager_macro, eager};
#[eager_macro]
macro_rules! op{
( plus ) => { + };
( minus ) => { - };
}
#[eager_macro]
macro_rules! integer{
( one ) => { 1 };
( two ) => { 2 };
}
macro_rules! calculate{
( $lhs:tt $op:tt $rhs:tt ) => {
eager!{integer!{$lhs} op!{$op} integer!{$rhs}}
};
}
let x = calculate!(one plus two);
assert_eq!(3, x);In this case, calculate! does not actually have to be eager!-enabled, since it is never used
inside an eager! block. That said, we probably should enable it so that others may later use
it in eager environments.