Created
January 27, 2019 18:47
-
-
Save tejasbubane/606e4e42643aa4e1db1779eaed6e3a67 to your computer and use it in GitHub Desktop.
My notes while learning Rust Macros
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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