# Rust Macros the right way

On this blog post I am going to talk about Macros, well Rust Macros, not the type of Macros you see on the picture above, so if you came in here looking for a tutorial on how much carbs, fats and proteins to ingest every day I am sorry to disappoint you. But back lets get back on track here, I would like to talk about specifically what are Macros? What type of Macros there are in Rust? And when to use them?

So the plan here is to start by defining what are Macros in a more general context and within the Rust language. Once we understand what they are we are going to take a deeper dive into the different types of macros we have in the Rust Programming Language and maybe throw in some examples in there, because theory without practice just does not stick. And to finish off the post I am going to talk about when to use Macros and some knows use cases out there.

### What are Macros?

In a more general context a Macro is just a shorthand for a programming term: macroinstruction. In the most basic terms, a macro is a way to automate a simple task. They can be used in a variety of different ways, from Microsoft Excel Macros to Automating tasks on your operating system.

#### What are Macros in the context of Rust programming?

A quote directly out of the [Rust Book](https://doc.rust-lang.org/book/ch19-06-macros.html), "Fundamentally, macros are a way of writing code that writes other code, which is known as *metaprogramming*." A lot of times in our day to day we write the exact same code over and over again and Macros are useful for reducing the amount of code you have to write and maintain, also helps to keep your code cleaner (if you use it right), which is also one of the roles of functions. However, macros have some additional powers that functions don’t, that we will get into it later in the blog post.

Let's look at an example. If you have been using Rust for more than a day you probably used the Macro *println!,* which as you may know prints a formatted text and add a *\\n (new line)* at the end for you.

```rust
fn main() {
    println!("Hello there.")
}
```

But have you ever stoped to think what goes on behind the scenes when you write that? Have you ever thought about what you would have to do to actually print a text to the console with the *println!* macro? Well, if you would like to print a list of text without using the *println!* this is one way you could do it.

```rust
// use Write trait that contains write() function  
use std::io::Write;  
  
fn main() {  
    std::io::stdout().write(b"Hello, world!\n").unwrap();  
}
```

Can you imagine having to write all that every time you needed to print one single line of text to the console? No thanks!

Well, and if you are curious, this is what the *println!* macro definition actually looks like:

```rust
#[macro_export]  
#[stable(feature = "rust1", since = "1.0.0")]  
#[cfg_attr(not(test), rustc_diagnostic_item = "println_macro")]  
#[allow_internal_unstable(print_internals, format_args_nl)]  
macro_rules! println {  
    () => {  
        $crate::print!("\n")  
    };  
    ($($arg:tt)*) => {{  
        $crate::io::_print($crate::format_args_nl!($($arg)*));  
    }};  
}
```

Reference: [https://doc.rust-lang.org/src/std/macros.rs.html#101](https://doc.rust-lang.org/src/std/macros.rs.html#101)

Even the macro is using Macros! Wow! But well, after you have that down, you life you get a lot easier for sure.

### Different Types of Macros

Rust has two types of macros:

1. **Declarative macros** sometimes referred to as "macros by example", “`macro_rules!` macros,” or just plain “macros”. *Declarative macros* enable you to write something similar to a match expression that operates on the Rust code you provide as arguments. It uses the code you provide to generate code that replaces the macro invocation.
    

We have already looked at a real example on how the *println!* is defined, but now lets look at how to define one of our own.

```rust
// use macro_rules! {}
macro_rules! add{
    // match like arm for macro
    ($a:expr,$b:expr)=>{
        // macro expand to this code
        {
            // $a and $b will be templated using the value/variable provided to macro
            $a+$b
        }
    }
}

fn main(){
    // call to macro, $a=1 and $b=2
    add!(1,2);
}
```

This code creates a macro that adds two numbers. The annotation `[[macro_rules!]](https://doc.rust-lang.org/rust-by-example/macros.html)` is used with the name of the macro, `add`, and the body of the macro.

The macro doesn’t really add two numbers, it just replaces itself with the code to add two numbers **at compile time**. Very similar to a match statement in this case the macro definition will look for the pattern that matches the parameters passed.

1. **Procedural macros** allow you to operate on the abstract syntax tree (AST) of the Rust code it is given. *Procedural macros* act more like functions in a sense. They accept some code as an input, operate on the code, and outputs some other piece of code matching against any pattern and replacing the code just like declarative macros do.
    

The three kinds of procedural macros:

* Attribute-like macros
    
* Custom derive macros
    
* Function-like macros (not covered on this blog post, unfortunetely)
    

Let's take a quick look into each of them…

To write a procedural macro, first we start by creating a project using `cargo new my-macro-lib --lib`. Once the project is ready, update the `Cargo.toml` to notify cargo the project will create procedural macros.

```ini
# Cargo.toml
[lib]
proc-macro = true
```

Now we are all set to venture into procedural macros.

#### Attribute macros:

*Attribute-like* macros helps you to create a custom attribute that attaches itself to an item and allows you to manipulate that item. Ohh and It can also take arguments!

```rust
#[some_attribute(some_argument)]
fn my_task(){
// some code
}
```

In the above code, `some_attribute` is an attribute macro. It manipulates the function `my_task`.

The function that defines a procedural macro takes a `TokenStream` as an input and produces a `TokenStream` as an output. The `TokenStream` type is defined by the `proc_macro` crate that we included above in the `Cargo.toml` and represents a sequence of tokens. This is the core of the macro: the source code that the macro is operating on makes up the input `TokenStream`, and the code the macro produces is the output `TokenStream`. To write a procedural macro, we also need to write our parser to parse `TokenStream`. The Rust community has a very good crate, `[syn](https://docs.rs/syn/1.0.53/syn/)`, for parsing `TokenStream`.

#### Custo Derive macros:

As you’ve seen Rust provides a mechanism called “derive” that lets you implement traits easily. Let's look at a quick example:

```rust
#[derive(Debug)]  
struct Point {  
    x: i32,  
    y: i32,  
}
```

That is a lot simpler then, don't you think?

```rust
struct Point {  
    x: i32,  
    y: i32,  
}
```

```rust
use std::fmt;
```

```rust
impl fmt::Debug for Point {  
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {  
        write!(f, "Point {{ x: {}, y: {} }}", self.x, self.y)  
    }  
}
```

Rust includes several traits that you can derive in his codebase, but it also lets you define your own.

Let’s build a very simple trait, and derive it with a custom derive.

The first thing we need to do is start a new crate for our project.

```rust
$ cargo new --bin hello-world
```

All we want is to be able to call `hello_world()` on a derived type. Something like this:

```rust
#[derive(HelloWorld)]  
struct Pancakes;
```

```rust
fn main() {  
    Pancakes::hello_world();  
}
```

With some kind of nice output, like `Hello, World! My name is Pancakes!`.

Let's start by writing what the use of our macro would look like from the user's perspective. Our `src/main.rs` would look like this:

```rust
#[macro_use]  
extern crate hello_world_derive;
```

```rust
trait HelloWorld {  
    fn hello_world();  
}
```

```rust
#[derive(HelloWorld)]  
struct FrenchToast;
```

```rust
#[derive(HelloWorld)]  
struct Waffles;
```

```rust
fn main() {  
    FrenchToast::hello_world();  
    Waffles::hello_world();  
}
```

With that we would expect the output:

```rust
Hello, World! My name is FrenchToast  
Hello, World! My name is Waffles
```

Great. Now that we know what we want let's write the procedural macro. At the moment, procedural macros need to be in their own library crate. As such, there’s a convention; for a crate named `foo`, a custom derive procedural macro is called `foo-derive`. Let's start a new crate called `hello-world-derive` inside our `hello-world` project.

```rust
$ cargo new hello-world-derive --lib
```

To make sure that our `hello-world` crate is able to find this new crate we've created, we'll add it to our toml:

```rust
[dependencies]  
hello-world-derive = { path = "hello-world-derive" }
```

Now let's look at the source of our `hello-world-derive` crate, here's one way it could look like:

```rust
extern crate proc_macro;  
extern crate syn;  
#[macro_use]  
extern crate quote;
```

```rust
use proc_macro::TokenStream;
```

```rust
#[proc_macro_derive(HelloWorld)]  
pub fn hello_world(input: TokenStream) -> TokenStream {  
    // Construct a string representation of the type definition  
    let s = input.to_string();  
      
    // Parse the string representation  
    let ast = syn::parse_derive_input(&s).unwrap();
```

```rust
 // Build the impl  
    let gen = impl_hello_world(&ast);  
      
    // Return the generated impl  
    gen.parse().unwrap()  
}
```

There is a lot going on here I know. Here we are making use of two crates: `[syn](https://crates.io/crates/syn)` and `[quote](https://crates.io/crates/quote)`. As you may have noticed, `input: TokenSteam` is immediately converted to a `String`. This `String` is a string representation of the Rust code for which we are deriving `HelloWorld`. At the moment, the only thing you can do with a `TokenStream` is convert it to a string, as far as I know at least (I may be wrong).

What we really need is to be able to *parse* Rust code into something usable. This is where `syn` comes to play. The other crate we've introduced is `quote`. It's essentially the dual of `syn` as it will make generating Rust code really easy. Remember? Input and Output…

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1724396281029/c7edbd23-636f-4554-aeb0-30522705afdc.jpeg align="left")

What we are doing here is we are taking a `String` of the Rust code for the type we are deriving, parsing it using `syn`, constructing the implementation of `hello_world` (using `quote`), then passing it back to Rust compiler.

One last note: you’ll see some `unwrap()`s there. If you want to provide an error for a procedural macro, then you should `panic!` with the error message, unfortunately. In this case, I am trying to keep it as simple as possible.

Awesome, we got this far, now let’s write `impl_hello_world(&ast)`.

```rust
fn impl_hello_world(ast: &syn::DeriveInput) -> quote::Tokens {  
    let name = &ast.ident;  
    quote! {  
        impl HelloWorld for #name {  
            fn hello_world() {  
                println!("Hello, World! My name is {}", stringify!(#name));  
            }  
        }  
    }  
}
```

So this is where quotes comes in. The `ast` argument is a struct that gives us a representation of our type (which can be either a `struct` or an `enum`). Check out the [docs](https://docs.rs/syn/0.11.11/syn/struct.DeriveInput.html), there is some useful information there. If you have worked with any type of parsing before, you may be familiar with ASTs. We are able to get the name of the type using `ast.ident`. The `quote!` macro (I know, a macro within a macro, feels we are in that inception movie, anyway…) lets us write up the Rust code that we wish to return and convert it into `Tokens`. `quote!` lets us use some really cool templating mechanics; we simply write `#name` and `quote!` will replace it with the variable named `name`. You can even do some repetition similar to regular macros work. You should check out the [docs](https://docs.rs/quote) for a good introduction.

So I think that’s it. Oh, well, we do need to add dependencies for `syn` and `quote` in the `Cargo.toml` for `hello-world-derive`.

```rust
[dependencies]  
syn = "0.11.11"  
quote = "0.3.15"
```

```rust
[lib]  
proc-macro = true
```

Ok so now, let’s compile `hello-world`. Executing `cargo run` now yields:

```rust
Hello, World! My name is FrenchToast  
Hello, World! My name is Waffles
```

That is it! We have built our first macro. I know it doesn't really do much, but we were able to show what it can do and now the ball is on your court.

### When to use Macros?

One of the advantages of using macros is that they don’t evaluate their arguments eagerly like functions do, which is one of the motivations to use macros other than functions.

A general *rule of thumb* is that macros can be used in situations where functions fail to provide the desired solution, where you have code that is quite repetitive, or in cases where you need to inspect the structure of your types and **generate code at compile time**. Taking examples from real use cases, Rust macros are used in a lot of cases, such as the following:

* Augmenting the language syntax by creating custom **Domain-Specific Languages** (**DSLs**)
    
* Writing compile time serialization code, like serde does
    
* Moving computation to compile-time, thereby reducing runtime overhead
    
* Writing and generating boilerplate code for repetitive tasks.
    

Lately I have been diving a lot into game development, specially with Unreal Engine and C++ and I guess that is why Macros were brought up to my attention so much. On game development with Unreal and C++ macros are a huge part of the development, they not only help keep you code clean and to the point, but also improve you productivity by a lot. Just like they do it in Rust.

### Conclusion

In this blog post we covered what are Macros in a more general context and also in the Rust programming language, we looked at the different types of Macros and also at some simple implementation examples and to finish it off we talked about when to use macros to help solve your coding problems and how it can help you to keep you code clean, organised and keep your productivity high.

I hope I was able to spike your interest on this topic and I curious to see how you will use macros from now on. Feel free to send me so I can take a look!

#### Like the post? How about a LIKE?
