Skip to content

Instantly share code, notes, and snippets.

@tejasbubane
Created January 27, 2019 18:47
Show Gist options
  • Save tejasbubane/606e4e42643aa4e1db1779eaed6e3a67 to your computer and use it in GitHub Desktop.
Save tejasbubane/606e4e42643aa4e1db1779eaed6e3a67 to your computer and use it in GitHub Desktop.
My notes while learning Rust Macros
// These are my notes from learning Rust Macros during a meetup
// https://www.meetup.com/rustox/events/256709420/
// I had not done any Rust before - but was still able to learn a lot!!
use std::collections::HashMap;
// All these macros run at compile time
// Simplest macro
macro_rules! hello {
() => {
println!("Hello World!");
}
}
macro_rules! yo {
// Unlike C macros (which are plain simple substitution,
// Rust macros allow pattern matching on arguments
// but they are still direct substitutions!
// expr is a pattern, there are multiple such patterns allowed
// like ident for identifier etc.
($name:expr) => {
println!("Yo {}!", $name);
};
}
macro_rules! hey {
// $(..)* means 0 or more
// $(..)+ means one or more
($($name:expr),*) => {{
$(println!("Hey {}!", $name);)*
}};
// Separator can be different
// This is pattern matching! Not just taking arguments like functions.
($($name:expr);*) => {{
$(println!("Yolo {}!", $name);)*
}};
}
macro_rules! my_print {
// straight literal match
(foo <> $e: expr) => {
println!("FoooooooFooooo!! {}", $e);
};
// match single expression
($e: expr) => {
println!("{}", $e);
};
// match identifier, expression
($i: ident, $e: expr) => {
let $i = {
let a = $e;
println!("{}", a);
a
};
}
}
// Ruby-style hashmap declaration
macro_rules! map {
($( $key:expr => $value:expr ), *) => {{
let mut hm = HashMap::new();
$( hm.insert($key, $value); )*
hm
}};
}
// Hygenic Macros
macro_rules! foo {
() => {
// This declaration does not affect caller
let x = 0;
}
}
// Ownership is not in play with macros
// it runs after macros have expanded
// This macro changes x from the caller scope when expanded
// Expansion is direct substitution
macro_rules! foo1 {
($x: ident) => {
$x = 0;
}
}
macro_rules! foo2 {
($z: ident) => {
let $z = 1;
}
}
#[allow(unused_variables)]
#[allow(unused_assignments)]
#[allow(unused_mut)]
fn main() {
hello!();
yo!("rustox");
hey!("just1");
hey!("yo1", "yo2");
hey!("yo1";"yo2");
// straight literal match
my_print!(foo <> "hello!");
// match single expression
my_print!(30 + 12);
// match identifier, expression
my_print!(x, 30 + 12);
// Ruby-style hashmap declaration
let m = map!(
"foo" => 1,
"bar" => 2
);
println!("{:?}", m);
let x = 4;
foo!();
println!("{}", x);
// Examples to show that macros are hygenic (unlike C)
let mut y = 5;
foo1!(y);
println!("{}", y);
let z = 20;
// We pass z as identifier - not the value - macros are compile-time!
foo2!(z);
// Since foo2 defines given identifer again and we pass z,
// its like replacing foo2 call with `let z = 1`
println!("{}", z);
let mut xx = 123;
// Macro has a closure (hence hygenic)
// It takes the surrounding environment
// This foo3 takes xx defined above
// It will not work if we move this macro outside main
// since there is no xx defined outside
macro_rules! foo3 {
() => {
xx = 0;
}
}
println!("{}", xx);
foo3!();
println!("{}", xx);
let mut xx = 33;
foo3!();
println!("{}", xx);
}
// good example for further ref: https://rreverser.com/writing-complex-macros-in-rust/
// All above examples are `functional` macros
// Another type of macros is `procedural` macros
// they take `TokenStream` as input and returns `TokenStream` as output
// Resources for procedural macros:
// Blog: https://tinkering.xyz/introduction-to-proc-macros/
// and related blogpost: https://github.com/zmitchell/wickerman
// Books:
// 1. https://danielkeep.github.io/tlborm/book/index.html
// 2. https://doc.rust-lang.org/1.7.0/book/macros.html
// Most blogs/books for procedural macros refer to older version `< v1.29`
// They got stable on `> v.1.3`.
// Recently released package `proc-macro-rules` allows defining
// procedural macros in functional style
// https://github.com/nrc/proc-macro-rules
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment