[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