My Rust journey: Macros!

Beggin with a simple, but straight, definition of Rust's macro: "code that writes code", It also has known as Metaprogramming. This turne the code more compact and less repetitive. It means that the Rust compiler, in compiling time, put the code of the macro inside Rust code.

In the learning process, some macros are used many times without conscientious with that, it is possible to suggest the use of macros is not so complicated.

A Rust's macro can be identified by the sign '!' on its end.

fn main() {
    // the vec! is a macro
    let my_vector = vec![1,2,3,4,5];
    for item in my_vector.iter() {
        // println! too
        println!("{}", item);
    }
}

To define a custom macro, it is needed to use another macro: macro_rules!.

// A macro definition seems like a 'match' statement
macro_rules! name {
    (pattern) => (expansion);
    (pattern) => (expansion);
...
}

This snippet needs an explanation...

The pattern contains an or more expressions '$arg:frag'. Where '&arg' is like a function argument with '$' sign and 'frag', fragment specifier, the "type" for '&arg'. There are a list of possible types to frag.

  • block
  • expr: it is used for expressions
  • ident: it is used for variable/function names
  • item
  • literal: it is used for literal constants
  • pat (pattern)
  • path
  • stmt (statement)
  • tt (token tree)
  • ty (type)
  • vis (visibility qualifier)
macro_rules! my_macro {
    ($arg:expr) => (println!("arg is {}", $arg));
}

Multiple arguments

Multiple arguments are also allowed into a macro expression definition with two different ways to use it.

One is like a normal function, defining each one $arg:frag and separating them with ','.

macro_rules! print_three {
    ($a:expr, $b:expr, $c:expr) => (println!("{}, {} and {}", $a, $b, $c);)
}

Other, that seams to me with JavaScript rest operator, what indicate that argument repeat a number of times defined by a sign. The expression for this approach changes a bit, but it is simple too. Basically, the new one is: ${pattern}, sign, where pattern has the older definition, $arg:frag, and sign can be: Either + or *.

  • * : Zero or more arguments
  • + : One or more arguments

Example from: The Rust Programming Language

// slightly simplified definition of the vec! macro
macro_rules! vec {
    ( $( $x:expr ),* ) => {
        {
            let mut temp_vec = Vec::new();
            $( // <-- 
                temp_vec.push($x);
            )* // <-- 
        temp_vec
        }
    };
}

That code has something to talk. The $(...)* is a generator of interactions for each argument 'x', zero or more times depending on how many 'arguments' has inside 'x' they 'runs' a loop through all items of 'x'.

Disclaimer: This is not explanation precise, it is just how I understood the concept.

Overloading

In OOP programming, overloading is the ability to create multiple functions of the same name with different implementations through divergent arguments. The Rust macro system has some like that, each '(pattern) ⇒ (expression);', arm, seams a function, considering the OOP analogy, where the pattern is the parameters to overload and expression is the implementation for that.

macro_rules! spy_or_not {
    // if just one parameter was setted
    ($name:literal) => ( println!("My name is {}", $name) );
    // if two was..
    ($name:literal, $surname:literal) => (println!("My name is {bonde}, {james} {bonde}", bonde=$surname, james=$name));}

fn main() {
    spy_or_not!("Henry"); // output: My name is Henry
    spy_or_not!("Henry", "Barreto"); // output: My name is Barreto, Henry Barreto
}

Visibility

Macros can be imported from a module to another, if as the origin module exports its through the attribute #[macro_export]. To work it, the use will solve the needs.

Example from: Rust 2018

pub mod foo {
    #[macro_export]
    macro_rules! bar {
        () => (println!("bar");)
    }
}
use foo::bar;

fn main() {
    bar!();
}

That is a simple summary of what I have learned about macros, indeed this all post series it is, but I guess whom someone will be helped if it likes a straight introduction to the theme.

Thank you for reading and feel free to comment or correct me, it will help a lot.

Did you find this article valuable?

Support Henry Barreto by becoming a sponsor. Any amount is appreciated!