Creating macros
Macro template
macro_rules! <name> {
(<args>) => {
<body>
};
}
<name>
can be any valid rust ident<args>
see below<body>
see below
Arguments
Arguments can be empty, or combinations of variables and variadic.
Each arg must be the format $<name>:<type>
. (i.e. $example:literal
)
<name>
can be any valid rust ident<type>
must be one of
type | description |
---|---|
literal | Rust literal, such as a number |
expr | A single expression |
ty | Rust type |
ident | Rust ident |
and others |
For example, this macro makes methods that adds two numbers:
#![allow(unused)] fn main() { macro_rules! add_numbers { ($method_name:ident, $num_type:ty) => { fn $method_name(lhs: $num_type, rhs: $num_type) -> $num_type { lhs + rhs } } } }
When called with add_number!(add_i32, i32);
this code is generated:
#![allow(unused)] fn main() { fn add_i32(lhs: i32, rhs: i32) -> i32 { lhs + rhs } }
Variadic
Arguments can be written as $( <arg> )*
for 0 or more and $( <arg> )+
for 1 or more values. (i.e. $( $name:literal )+
), this would allow example!(1 2 3)
.
To support commas you can write $( <arg> ),*
, this would allow example!(1,2,3)
.
Optional
Anything can be optional using this syntax $( <thing> )?
, this include commas, such as
- optional trailing comma:
macro_rules! example($var1:literal, $var2:literal $(,)?)
- optional commas in variadic:
macro_rules! list($( $items:literal )$(,)?*)
Body
The body must be an expression or single line, to support multiple lines surround the code in {}, this is often written as
macro_rules! <name> {
(<args>) => {{
<body>
}};
}
Variadic
To use these arguments you have to surround them in $( <name> )*
in the body.
Anything can be written in the parentheses and it will be repeated once per item in <name>
For example if you want to print each item on a different line:
#![allow(unused)] fn main() { macro_rules! print_nums { ($( $numbers:literal ),+) => {{ $( println!("{}", $numbers); )* }}; } }
If called with print_nums(1,2,3);
This generates
#![allow(unused)] fn main() { println!("{}", 1); println!("{}", 2); println!("{}", 3); }
Optional
Optional arguments should also be surrounded like $( <name> )*
, anything can be written inside like before.
When dealing with optional args you may need to get an alternative value
#![allow(unused)] fn main() { macro_rules! add_nums { ($num1: expr $(, $num2: expr)?) => { $num1 + $( $num2 )* }; } }
This works fine if called with add_nums!(1,2)
but with add_nums!(1)
the code generated would be 1 +
which fails to compile.
To get around this use something to offer a substitute such as
#![allow(unused)] fn main() { macro_rules! some_or_none { () => { None }; ($entity:expr) => { Some($entity) } } //or macro_rules! or_else { ($value:literal, $other: literal) => { $value}; (, $other: literal) => { $other }; } }
These are used like this
#![allow(unused)] fn main() { macro_rules! add_nums { ($( $num1: expr )? $(, $num2: expr)?) => { or_else!($( $num1 )*, 0) + or_else!($( $num2 )*, 0) }; } }
Overloading
Macros can support different argument sets:
#![allow(unused)] fn main() { macro_rules! add_nums { ($num1:literal, $num2:literal) => { $num1 + $num2 }; ($num1:literal, $num2:literal, $num3:literal) => { $num1 + $num2 + $num3 }; } }
This can be called with add_nums!(1,2);
and add_nums!(1,2,3);
Hygiene
Macros have 'hygiene' which means to access something from your crate you'll have to spell out the full path:
$crate::path::some_method(..)
$crate
refers to your crate.
Visibility
Macros have to be annotated with #[macro_export]
for it to be usable by other modules/crates.
Advanced
Macros will accept extra text which can be required to invoke the macro:
#![allow(unused)] fn main() { macro_rules! add_nums { ($num1: literal + $num2: expr) => { $num1 + $num2 }; (($num1: expr) + $num2: expr) => { $num1 + $num2 }; } }
Would have be called like this add_nums!(1 + 2)
or add_nums!((some_var) + 2)
With custom text in the arguments then either commas are needed to separate them
macro_rules! example($thing1:expr, $thing2:expr)
or all but the last expr must be declared and called surrounded by parentheses macro_rules! example(($thing1:expr) $thing2:expr)
and example!((thing1) thing2);
See more Macros by example