First steps with Rust declarative macros!
Tags: beginners, rust, tutorial, codenewbie
Published: 2021-07-26 14:09:13

Macros are one of the ways to extend Rust syntax. As [_The Book_](https://doc.rust-lang.org/book/ch19-06-macros.html) calls it, “a way of writing code that writes other code”. Here, I will talk about _declarative macros_, or as it is also called, _macros by example_. Examples of declarative macros are `vec!`, `println!` and `format!`. 

The macros I will **not** talk about are the _procedural macros_, but you can read about them [here](https://doc.rust-lang.org/reference/procedural-macros.html).

As usual, **I am writing for beginners**. If you want to jump to the next level, check:
- _The Book's_ [section on macros](https://doc.rust-lang.org/book/ch19-06-macros.html).
- The chapter about macros in [_Rust by Example_](https://doc.rust-lang.org/reference/macros.html)
- [_The Little Book of Rust Macros_](https://danielkeep.github.io/tlborm/book/README.html), which is the most complete material I found about the topic (the second chapter is specially amusing).

---

## Why do I need macros?
> The actual coding start in the [next section](#coding).

Declarative macros (from now on, just “macros”) are not functions, but it would be silly to deny the resemblance. Like functions, we use them to perform actions that would otherwise require too many lines of code or quirky commands (I am thinking about `vec!` and `println!`, respectively). These are (two of) the reasons to _use_ macros, but what about the reasons to _create_ them? 

Well, maybe you are developing a crate and want to offer this feature to the users, like warp did with [`path!`](https://docs.rs/warp/0.1.20/warp/macro.path.html). Or maybe you want to use a macro as a boilerplate, so you don't have to create several similar functions, as I did [here](https://github.com/rogertorres/mtgsdk/blob/main/src/cards.rs#L197). It might be also the case that you need something that cannot be delivered by usual Rust syntax, like a function with initial values or structurally different parameters (such as `vec!`, that allows calls like `vec![2,2,2]` or `vec![2;3]`—more on this later).

That being said, I believe that the best approach is to learn how to use them, try them a few times, and when the time comes when they might be useful, you will remember this alternative.

---

## <a name="coding"></a>Declaring macros
This is how you declare a macro:
```rust
macro_rules! etwas {
    () => {}
}
```

You could call this macro with the commands `etwas!()`, `etwas![]` or `etwas!{}`. There's no way to force one of those. When we call a macro always using one or the other—like parenthesis in `println!("text")` or square-brackets in `vec![]`—it is just a convention of usage (a convention that we should keep).

But what is happening in this macro? Nothing. Let's add something to make it easier to visualize its structure:

```rust
macro_rules! double {
    ($value:expr) => { $value * 2 }
}

fn main() {
    println!("{}", double!(7));
}
```

The left side of `=>` is the **matcher**, the rules that define what the macro can receive as input. The right side is the **transcriber**, the output processing. 

> Not very important, but both matcher and transcriber could be writen using either `()`, `[]` or `{}`.

---

## The matching
This will become clear later, but let me tell you right the way: the matching resembles [regex](https://en.wikipedia.org/wiki/Regular_expression). You may ask for specific arguments, fixed values, define acceptable repetition, etc. If you are familiar with it, you should have no problems picking this up.

Let's go through the most important things you should know about the matching.

### Variable argument
Variable arguments begin with `$` (e.g., `$value:expr`). Their structure is: `$` `name` `:` `designator`. 
- Both `$` and `:` are fixed.
- The `name` follows Rust variables convention. When used in the transcriber (see below), they will be called _metavariables_.
- Designators are **not** variable types. You may think of them as “syntax categories”. Here, I will stick with [expressions](https://doc.rust-lang.org/reference/expressions.html) (`expr`), since Rust is [“primarily an expression language”](https://doc.rust-lang.org/reference/statements-and-expressions.html). A list of possible designators can be found [here](https://doc.rust-lang.org/reference/macros-by-example.html#metavariables). 

> **Note:** There seems to be no consensus on the name "designator". [The little book](https://danielkeep.github.io/tlborm/book/mbe-min-captures-and-expansion-redux.html) calls it "capture"; [The Rust reference](https://doc.rust-lang.org/reference/macros-by-example.html) calls it "fragment-specifier"; and you will also find people referring them as "types". Just be aware of that when jumping from source to source. Here, I will stick with designator, as proposed in [Rust by example](https://doc.rust-lang.org/rust-by-example/macros/designators.html).

### Fixed arguments
No mystery here. Just add them without `$`. For example:
```rust
macro_rules! power {
    ($value:expr, squared) => { $value.pow(2) }
}

fn main() {
    println!("{}", power!(3_i32, squared));
}
```

I know there are things here that I have not explained yet. I will talk about them now.

### Separator
Some designators require some specific follow up. Expressions require one of these:  `=>`, `,` or `;`. That is why I had to add a comma between `$value:expr` and the fixed-value `squared`. You will find a complete list of follow-ups [here](https://danielkeep.github.io/tlborm/book/mbe-min-captures-and-expansion-redux.html).

### Multiple matching
What if we want our macro to not only calculate a number squared, but also a number cubed? We do this:
```rust
macro_rules! power {
    ($value:expr, squared) => { $value.pow(2_i32) }
    ($value:expr, cubed) => { $value.pow(3_i32) }
}
```

Multiple matching can be used to capture different levels of specificity. Usually, you will want to write the matching rules from the most-specific to the least-specific, so your call doesn't fall in the wrong matching. A more technical explanation can be found [here](https://danielkeep.github.io/tlborm/book/mbe-min-captures-and-expansion-redux.html).

### Repetition
Most macros that we use allow for a flexible number of inputs. For example, we may call `vec![2]` or `vec![1, 2, 3]`. This is where the matching resembles Regex the most. Basically, we wrap the variable inside `$()` and follow up with a repetition operator:
- `*` — indicates any number of repetitions.
- `+` — indicates any number, but at least one.
- `?` — indicates an optional, with zero or one occurrence.

Let's say we want to add `n` numbers. We need at least two addends, so we will have a single first value, and one or more (`+`) second value. This is what such a matching would look like.

```rust
macro_rules! adder {
    ($left:expr, $($right:expr),+) => {}
}

fn main() {
    adder!(1, 2, 3, 4);
}
```

We will work on the transcriber latter.

### Repetition separator
As you can see in the example above, I added a comma before the repetition operator `+`.  That's how we add a separator for each repetition without a trailing separator. But what if we want a trailing separator? Or maybe we want it to be flexible, allowing the user to have a trailing separator or not? You may have any of the three possibilities like this:

```rust
macro_rules! no_trailing {
    ($($e:expr),*) => {}
}

macro_rules! with_trailing {
    ($($e:expr,)*) => {}
}

macro_rules! either {
    ($($e:expr),* $(,)*) => {}
}

fn main() {
    no_trailing!(1, 2, 3);
    with_trailing!(1, 2, 3,);
    either!(1, 2, 3);
    either!(1, 2, 3,);
}
```

### Versatility
Unlike functions, you may pass rather different arguments to macros. Let's consider the `vec!` macro example. For that, I will omit the transcriber.

```rust
macro_rules! vec {
    () => {};
    ($elem:expr; $n:expr) => {};
    ($($x:expr),+ $(,)?) => {};
}
```

It deals with three kinds of calls:
- `vec![]`, which creates an empty Vector.
- `vec!["text"; 10]`, which repeats the first value ("text") `n` times, where `n` is the second value (10).
- `vec![1,2,3]`, which creates a vector with all the listed elements.

> If you want to see the implementation of the `vec!` macro, check [Jon's stream about macros](https://www.youtube.com/watch?v=q6paRBbLgNw).

---

## The transcriber
The magic happens after the `=>`. Most of what you are going to do here is regular Rust, but I would like to bring your attention to some specificities.

### Type
When I called the exponentiation macro `power!`, I did this:
```rust
power!(3_i32, squared);
```

I had to specify the type `i32` because I used the `pow()` function, which cannot be called on ambiguous numeric type; and as we do not define types in macros, I had to let the compiler know this information somehow. This is something to be aware when dealing with macros. Of course, I could have forced it by declaring a variable and passing the metavariable value to it and thus fixing the variable type. However, to do such a thing, we need multiple statements.

### Multiple statements
To have more than one line in your transcriber, you have to use double curly brackets:
```rust
macro_rules! etwas {
                             //v --- this one
    ($value:expr, squared) => {{ 
        let x: u32 = $value;
        x.pow(2)
    }}
   //^ --- and this one
};
```
Easy.

### Using repetition
Let us finish our `adder!` macro.

```rust
macro_rules! adder {
    ($($right:expr),+) => {{
        let mut total: i32 = 0;
        $( 
            total += $right;
        )+
        total
    }};
}

fn main() {
    assert_eq!(adder!(1, 2, 3, 4), 10);
}
```

To handle repetition, all we have to do is to place the statement we want to repeat within `$()+` (the repetition operator should match, that is why I am using `+` here as well). 

But what if we have multiple repetitions? Consider the following code.
```rust
macro_rules! operations {
    (add $($addend:expr),+; mult $($multiplier:expr),+) => {{
        let mut sum = 0;
        $(
            sum += $addend;
         )*

         let mut product = 1;
         $(
              product *= $multiplier;
          )*

          println!("Sum: {} | Product: {}", sum, product);
    }} 
}

fn main() {
    operations!(add 1, 2, 3, 4; mult 2, 3, 10);
}
```

How does Rust know that it must repeat _four times_ during the first repetition block and only _three times_ in the second one? By context. It checks the variable that is being use and figure out what to do. Clever, huh?

Sure, you can make things harder to Rust. In fact, you may turn them indecipherable, like this:

```rust
macro_rules! operations {
    (add $($addend:expr),+; mult $($multiplier:expr),+) => {{
        let mut sum = 0;        
        let mut product = 1;
            
        $(
            sum += $addend;
            product *= $multiplier;
        )*

          println!("Sum: {} | Product: {}", sum, product);
    }} 
}
```

What does “clever Rust” do with something like this? Well, one of the things it does best: it gives you a clear compile error:

```zsh
error: meta-variable 'addend' repeats 4 times, but 'multiplier' repeats 3 times
  --> src/main.rs:43:10
   |
43 |           $(
   |  __________^
44 | |             sum += $addend;
45 | |             product *= $multiplier;
46 | |         )*
   | |_________^
```

Neat! 🦀

---

## Expand
As mentioned earlier, macros are syntax extensions, which means that Rust will turn them into regular Rust code. Sometimes, to understand what is going ~~wrong~~ on, it is very helpful to see how rust pull that transformation off. To do so, use the following command.

```rust
$ cargo rustc --profile=check -- -Zunstable-options --pretty=expanded
```

This command, however, is not only verbose, but it will also call for the nightly compiler. To avoid this and get the same result, you may install [`cargo-expand`](https://github.com/dtolnay/cargo-expand):

```shell
$ cargo install cargo-expand
```

Once it is installed, you just have to run the command `cargo expand`.

> **Note**: Although you don't have to be using the nightly compiler, I guess (and you may call me on this) you got to have it installed. To do so, run the command `rustup instal nightly`.

Look at how the macro `operations!` is expanded.
```rust
fn main() {
    {
        let mut sum = 0;
        sum += 1;
        sum += 2;
        sum += 3;
        sum += 4;
        let mut product = 1;
        product *= 2;
        product *= 3;
        product *= 10;
        {
            ::std::io::_print(::core::fmt::Arguments::new_v1(
                &["Sum: ", " | Product: ", "\n"],
                &match (&sum, &product) {
                    (arg0, arg1) => [
                        ::core::fmt::ArgumentV1::new(arg0, ::core::fmt::Display::fmt),
                        ::core::fmt::ArgumentV1::new(arg1, ::core::fmt::Display::fmt),
                    ],
                },
            ));
        };
    };
}
``` 

As you can see, even `println!` was expanded.

---

## Export and import
To use a macro outside the scope it was defined, you got to export it by using `#[macro_export]`.

```rust
#[macro_export]
macro_rules! etwas {
    () => {};
}
```

You may also export a module of macros with `#[macro_use]`.
```rust
#[macro_use]
mod inner {
    macro_rules! a {
        () => {};
    }

    macro_rules! b {
        () => {};
    }
}

a!();
b!();
```

To use a macro that a crate exports, you also use `#[macro_use]`.

```rust
#[macro_use(lazy_static)] // Or #[macro_use] to import all macros.
extern crate lazy_static;

lazy_static!{}
```

The example above is from [The Rust Reference](https://doc.rust-lang.org/reference/macros-by-example.html#the-macro_use-attribute).

---

And that's all for today. There is certainly more to cover, but I will leave you with the readings I recommended earlier.

> _Cover image by [Thom Milkovic](https://unsplash.com/photos/FTNGfpYCpGM)_.