Macros

Rust provides a powerful macro system that allows metaprogramming. As you've seen in previous chapters, macros look like functions, except that their name ends with a bang !, but instead of generating a function call, macros are expanded into source code that gets compiled with the rest of the program.

Macros are created using the macro_rules! Macro.

() -- takes no argument ($argument: designator)

Popular designators are

expr is used for expressions

ty is used to type

ident is used for variable/function names

Syntax

macro_rules! <name of macro>{
    () => {}
}
() - Match for the pattern
{} - Expand the code / Body of the macro
// Macro with No argument

macro_rules! print_hello {
    () => {
        // The macro will expand into the contents of this block.
        println!("Hello World");
    };
}

fn main() {
    print_hello!();
}

Macro returning a constant value

// Macro returning value 10

macro_rules! ten {
    () => {5 + 5};
}

fn main(){
    println!("{}",ten!());
}

Macro with one argument

using expr designator

// Macro with one argument

macro_rules! hi {
    ($name:expr) => {
        println!("Hello {}!",$name);
    };
}

fn main() {
    hi!("Rachel");
}

Simple addition macro

// Takes two arguments

macro_rules! add{
    ($a:expr,$b:expr)=>{
         {
            $a+$b
        }
    }
}

fn main(){
    let c = add!(1,2);
    println!("{c}");
}

Demo Stringify

// Stringify
//In Rust, stringify! is a macro that takes a Rust expression 
//and converts it into a string literal 

fn main() {
    println!("{},{}",1+1,stringify!("1+1"));
}

Macro with Expressions

// More Expressions

macro_rules! print_result {
    ($expression:expr) => {
        println!("{:?} = {:?}", stringify!($expression), $expression);
    };
}

fn main() {
    print_result!(1 + 1);

    // Recall that blocks are expressions too!
    print_result!({
        let x = 10; x * x + 2 * x - 1
    });
}

using expr and ty designators

// multiple designators
// variables with different datatypes can be added using this macro

macro_rules! add_using{
    // using a ty token type for matching datatypes passed to the macro
    
    ($x:expr,$y:expr,$typ:ty)=>{
        $x as $typ + $y as $typ
    }
}

fn main(){
    let i:u8 = 5;
    let j:i32 = 10;
    println!("{}",add_using!(i,j,i32));
}

Repeat / Dynamic Arguments

($($v:expr),*)  - Here the star (*) will repeat the patterns inside $()
And comma is the separator.
// Repeat / Dynamic number of arguments

macro_rules! hi {
    ($($name:expr),*) => {
        {
            //let mut n = Vec::new();
            $(
                println!("Hi {}!",$name);
            )*
        }
    };
}

fn main() {
    hi!("Rachel","Ross","Monica");
}

Remember vec! Macro?

Let's try to create our equivalent of it.

// Creating vec! equivalent 

macro_rules! my_vec {
    (
        $( $name:expr ), * ) => {
        {
            let mut n = Vec::new();
            $( n.push($name); )*
            
            n
        }
    };
}

fn main() {
    println!("{:?}",my_vec!("Rachel","Ross","Monica"));
}

Repeat - with Numeric arguments

macro_rules! add_all{
    ($($a:expr) , *) => 
    {
    // initial value of the expression
    0
    // block to be repeated
    $(+$a)*
    }
}

//The * in $(+$a)* is a repetition operator, meaning 
//"repeat +$a for each $a matched".

fn main(){
    println!("{}",add_all!(1,2,3,4));
    //println!("{}",add_all!());
}

Compile time Assertions

macro_rules! assert_equal_len {
    ($a:expr, $b:expr) => {
        assert!($a.len() == $b.len(), "Arrays must have the same length");
    };
}

fn main() {
    let a1 = [1, 2, 3];
    let a2 = [4, 5, 6];
    assert_equal_len!(a1, a2); // Compiles fine

    //let a3 = [7, 8];
    //assert_equal_len!(a1, a3); // This will fail to compile
}

Macro Overloading

Overload to accept different combinations of arguments

// `check!` will compare `$left` and `$right`
// in different ways depending on how you invoke it:

macro_rules! check {

    ($left:expr; and $right:expr) => {
        println!("{:?} and {:?} is {:?}",
                 stringify!($left), stringify!($right), $left && $right)
    };

    ($left:expr; or $right:expr) => {
        println!("{:?} or {:?} is {:?}",
                 stringify!($left), stringify!($right), $left || $right)
    };
}

fn main() {
    check!(1i32 + 1 == 2i32; and 2i32 * 2 == 4i32);
    check!(true; or false);
}