[Avg. reading time: 11 minutes]

Macros

Rust macros enable metaprogramming. They generate code at compile time instead of executing at runtime.

Macros look like functions but end with !. Instead of calling code, they expand into code before compilation.

Why Macros?

  • Reduce repetitive code
  • Enable flexible APIs (like println!, vec!)
  • Work with variable number of arguments
  • Operate at compile time

Defining Macros

Macros are defined using macro_rules!

macro_rules! macro_name {
    (pattern) => {
        expansion
    };
}
  • pattern : matches input
  • expansion : generated code

Designators (Matchers)

These define what kind of input the macro accepts:

expr : expressions (1 + 2, variables, function calls)
ty : types (i32, String)
ident : identifiers (variable/function names)

Macro with No Arguments

macro_rules! print_hello {
    () => {
        println!("Hello World");
    };
}

fn main() {
    print_hello!();
}

Returning Values

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

fn main() {
    println!("{}", ten!());
}
macro_rules! si {
    ($p:expr, $n:expr, $r:expr) => {
        ($p * $n * $r) / 100
    };
}

fn main() {
    println!("{}", si!(1000, 1, 10));
}

$p:expr, $n:expr, $r:expr : not variables, :expr ensures its Rust Expression

Macro with String argument

using expr designator

macro_rules! hi {
    ($name:expr) => {
        println!("Hi {} How are you?",$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

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

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

    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);
}

Summary

  • Macros match patterns -> generate code
  • They run at compile time, not runtime
  • Use macros when:
    • You need repetition elimination
    • You need flexible syntax
  • Avoid macros when:
    • A function or generic can solve it
    • Readability suffers

#macros #overloadingVer 2.0.22

Last change: 2026-04-01