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
typedescription
literalRust literal, such as a number
exprA single expression
tyRust type
identRust 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