-Why write one? - sometimes inline code is more optimized, still we cannot have it to be written everytime in code. - So since macro's get expanded compile time. They can be treated as blocks which can be added, letting compiler expand on runtime.
macro_rules! a_marco {
() => (
println!("Oh My Macro.!")
)
}
// macro with params
macro_rules! x_and_y {
(x => $e:expr) => (println!("X: {}", $e));
(y => $e:expr) => (println!("Y: {}", $e));
// expr is an expression
}
fn main() {
a_marco!();
x_and_y!(x=>10);
x_and_y!(y=>20+30);
}
- Another simple thought, where macro's can be used are in building DSL's, since one can write LISPy code using macro
- some custom marco example
macro_rules! build_fn {
($func_name:ident) => (
fn $func_name(){
println!("you called {:?}()", stringify!($func_name));
}
)
}
macro_rules! print_ex {
($e:expr) => (
println!("{:?} = {:?}", stringify!($e), $e);
)
}
fn main() {
build_fn!(Hi_there);
Hi_there();
print_ex!({
let y = 20;
let z= 40;
z+y+20*78+10
})
}
- macro with the pattern matching
macro_rules! exame {
($l:expr; and $r:expr) => (
println!("{:?} and {:?} is {:?}",
stringify!($l),
stringify!($r),
$l && $r)
);
($l:expr; or $r:expr) => (
println!("{:?} and {:?} is {:?}",
stringify!($l),
stringify!($r),
$l || $r)
);
($l:expr; something $r:expr) => (
println!("{:?} something {:?} is {:?}",
stringify!($l),
stringify!($r),
$l || $r)
);
}
fn main() {
exame!(1==1; and 2==1+1);
exame!(1==1; something 2==1+1);
}
// Note: something is not a language keyword neither a variable
- macro for magical expansions: Python like list comprehension
macro_rules! compr {
($id1: ident | $id2: ident <- [$start: expr; $end: expr] , $cond: expr)=>{
{
let mut vec = Vec::new();
for num in $start..$end + 1 {
if $cond(num) {
vec.push(num);
}
}
vec
}
};
}
fn even(x: i32) -> bool { x % 2 == 0 }
fn odd(x: i32) -> bool { x % 2 != 0 }
fn main() {
let evens = compr![x | x <- [1;10], even];
println!("{:?}", evens);
let odds = compr![y | y <- [1;10], odd];
println!("{:?}", odds);
}
- find min macro
// `find_min!` will calculate the minimum of any number of arguments.
macro_rules! find_min {
// Base case:
($x:expr) => ($x);
// `$x` followed by at least one `$y,`
($x:expr, $($y:expr),+) => (
// Call `find_min!` on the tail `$y`
std::cmp::min($x, find_min!($($y),+))
)
}
fn main() {
println!("{}", find_min!(1));
println!("{}", find_min!(1+2, 2));
println!("{}", find_min!(5, -2, 4));
}
- DSL example - Simple calculator
macro_rules! calculate {
(eval $e:expr) => {{
{
let val: usize = $e; // Force types to be integers
println!("{} = {}", stringify!{$e}, val);
}
}};
}
fn main() {
calculate! {
eval 1 + 2 // hehehe `eval` is _not_ a Rust keyword!
}
calculate! {
eval (1 + 2) * (3 / 4)
}
}
- complex variadic calculator DSL
macro_rules! calculate {
// The pattern for a single `eval`
(eval $e:expr) => {{
{
let val: usize = $e; // Force types to be integers
println!("{} = {}", stringify!{$e}, val);
}
}};
// Decompose multiple `eval`s recursively
(eval $e:expr, $(eval $es:expr),+) => {{
calculate! { eval $e }
calculate! { $(eval $es),+ }
}};
}
fn main() {
calculate! { // Look ma! Variadic `calculate!`!
eval 1 + 2,
eval 3 + 4,
eval (2 * 3) + 1
}
}
- some concepts/learnings from here
- meta-variables for meta programming
- Building a hashmap shorthand macro using macro
use std::collections::HashMap;
macro_rules! hash_map {
($($key: expr => $val: expr),*) => { //, after before * allows us to put , in usage between values
{
let mut map = HashMap::new();
$(
map.insert($key, $val);
)*
map
}
}
}
fn main() {
let map = hash_map!{
"one"=>1,
"two"=>2,
"three"=>3
};
println!("{:?}",map)
}
// *, + are wild card used in variadic expressions
- fully functional macro lisp here