# How macros make your Rust life easier and generally better

How macros make your life easier

Hello fellow Rustaceans!

Let’s explore a topic that has been quite foreign to me for a long time: macros. After reading this post you’ll be able to automate all kinds of tasks. (Or skip the reading and go to GitHub directly)

Like everybody, I dread copy-paste programming of boiler plate code. However I also never looked into macros, partly because the C/C++ macro engine was still in the back of my mind, operating as a fancy search-and-replace step before compilation; partly because Rust’s macros looked complex and I didn’t want to deal with that… . But I was wrong. Totally wrong in fact - macros make your code more efficient, less repetitive, and in general better without much complexity at all.

## What Rust macros are

Contrary to the C preprocessor, Rust’s macros are not simple text replacements - but part of the normal compilation process. This means they behave more like functions, inserted into the code before it is compiled to binary - not as text but directly into the AST (or in other words it’s programming programming - or metaprogramming). In this realm there is already some type-safety, making unexpected behavior rare. The effects can be seen when you tinker a little bit with the upcoming examples - the macro will compile just fine, but the compiler is going to complain when using invalid types.

Additionally, there are two types of macros: procedural and declarative. Procedural macros are more complex to write but (IMHO) easier to use - we have all used #[derive(Debug)] to make the Debug trait implementation magically appear. Yet I want to leave those for another time and explore declarative macros here (those with the ! in their names). Let’s stat off simple: how to declare a declarative macro?

## Declaring declarative macros

As appropriate, a macro is declared using a macro. macro_rules! is the birthplace for every macro. Let’s create a very simple macro that returns the result of a simple calculation: 1 + 1.

macro_rules! two  {
() => { 1 + 1 }
}

assert_eq!(2, two!());


As any macro, this has three parts:

• a name (two)
• an (empty) matcher (())
• a transcriber containing an expression ({ 1 + 1 })

The name of the “matcher” gives away its purpose: it matches the invocation and replaces it with the transcriber. The matching is done exactly (as we’ll see further down) and a macro’s name can have several matchers. This gives you the power to create domain specific languages and extend Rust’s syntax. Let’s update the macro above:

macro_rules! calc  {
(two) => { 1 + 1 };
(three) => { 1 + 2 }
}

assert_eq!(2, calc!(two));
assert_eq!(3, calc!(three));


Note that two or three are just … words that match a pattern in the macro declaration. Any combination of characters can do the same.

So now that we know about declaring macros and what they do, how about we add some complexity with parameters? Here is an example:

macro_rules! repeat_n_times {
($n:expr,$text:expr) => {(0..$n).map(|_| format!("{}",$text)).collect::<Vec<String>>()}
}

assert_eq!(vec!["Hello", "Hello", "Hello"], repeat_n_times!(3, "Hello"));


$(s.insert($key, $value);)* s } }; } let actual = key_value!(BTreeMap<&str, usize>, "hello" => 2, "world" => 1); let mut desired = BTreeMap::new(); desired.insert("hello", 2); desired.insert("world", 1); assert_eq!(actual, desired); // or a different kind of map: let actual = key_value!(HashMap<&str, usize>, "hello" => 2, "world" => 1); let mut desired = HashMap::new(); desired.insert("hello", 2); desired.insert("world", 1); assert_eq!(actual, desired);  ## Import-Export business Having all these macros living freely in the files can become messy quickly and they should be grouped together - the same way that functions/structs/… are kept in modules to make them a lot more managable. Macros are not exported by default, so the attribute #[macro_export] on top of the macro takes care of selectively exporting the macro to outside the module, similar to what pub does. Here is the same example from above, with the macro in a submodule: mod helpers { #[macro_export] // try removing this to see the error macro_rules! key_value { ($cls: ty, $($key:expr => $value:expr ),* ) => { { let mut s = <$cls>::new();
$( s.insert($key, \$value);
)*
s
}
};
}
}

let actual = key_value!(BTreeMap<&str, usize>, "hello" => 2, "world" => 1);
let mut desired = BTreeMap::new();
desired.insert("hello", 2);
desired.insert("world", 1);
assert_eq!(actual, desired);

// or a different kind of map:

let actual = key_value!(HashMap<&str, usize>, "hello" => 2, "world" => 1);
let mut desired = HashMap::new();
desired.insert("hello", 2);
desired.insert("world", 1);
assert_eq!(actual, desired);


And that’s it! Creating macros is done, however when using macros from an unknown source you can also declare a #[macro_use] attribute over an extern crate mycrate (yes, also in the Rust 2018 Edition) to stay safe and only import a subset.

## Go, be lazy

This is it - how to safe time and effort with Rust macros. Since there is no learning like tinkering and playing with the thing you want to be good at, so check out the GitHub repository for this post. For any additional inspiration, here are some things that you can (and should?) use macros for: