基本使用
- 可以选择括号:( )、{ }、[ ],随便选
- 格式:(matcher) => (transcriber)

// 声明一个新的宏,命名为 my_vecmacro_rules! my_vec {// () is our first matcher// 由于它是空的,它将匹配不带任何参数的宏调用() => [ Vec::new() ];// (make an empty vec) is our second matcher// 当我们的输入字面上 “make a empty vec” 时,它就会匹配(make an empty vec) => ( Vec::new() );// 接受一个表达式 (expr) 并将其绑定到 x{ $x:expr } => {{let mut vec = Vec::new();vec.push($x);vec}};// 接受多个用逗号分隔的表达式,同样的将它们绑定到 x[ $($x:expr),* ] => ({let mut vec = Vec::new();$(vec.push($x);)*vec});}
#[macro_use]mod my_vec;fn main() {let empty: Vec<i32> = my_vec![];println!("Empty vector: {:?}", empty);let also_empty: Vec<&str> = my_vec!("make an empty vec");println!("also_empty vector: {:?}", also_empty);let three_numbers: Vec<i32> = my_vec!(1, 2, 3);println!("three_numbers: {:?}", three_numbers);}
也可以写在同一个文件中,但宏要标明导出,如下所示
fn main() {let empty: Vec<i32> = my_vec![];println!("Empty vector: {:?}", empty);let also_empty: Vec<&str> = my_vec!("make an empty vec");println!("also_empty vector: {:?}", also_empty);let three_numbers: Vec<i32> = my_vec!(1, 2, 3);println!("three_numbers: {:?}", three_numbers);}// 声明一个新的宏,命名为 my_vec#[macro_export]macro_rules! my_vec {// () is our first matcher// 由于它是空的,它将匹配不带任何参数的宏调用() => [ Vec::new() ];// (make an empty vec) is our second matcher// 当我们的输入字面上 “make a empty vec” 时,它就会匹配(make an empty vec) => ( Vec::new() );// 接受一个表达式 (expr) 并将其绑定到 x{ $x:expr } => {{let mut vec = Vec::new();vec.push($x);vec}};// 接受多个用逗号分隔的表达式,同样的将它们绑定到 x[ $($x:expr),* ] => ({let mut vec = Vec::new();$(vec.push($x);)*vec});}
可变参数和默认参数
Rust 函数不允许可变参数。一个原因可能是可变参数让编译器的生活变得更加困难。或者,它可能不是一个足够重要的功能。可以用宏实现

pub fn base_greeting_fn(name: &str, greeting: &str) -> String {format!("{} {}!", greeting, name)}macro_rules! greeting {($name: literal) => {base_greeting_fn($name, "Hello")};($name: literal, $greeting: literal) => {base_greeting_fn($name, $greeting)};}
// 没有这个 use,宏那边的文件会报找不到 base_greeting_fn 函数的错误use crate::greeting::base_greeting_fn;#[macro_use]mod greeting;fn main() {let greet = greeting!("hbj", "rust");println!("{}", greet);let greet_with_default = greeting!("hbj");println!("{}", greet_with_default);}
展开宏代码的方式
- 方式一:使用 宏追踪(trace macros)**#![feature(trace_macros)]**
// rustup default nightly 或者 cargo +nightly your-command#![feature(trace_macros)]use crate::greeting::base_greeting_fn;#[macro_use]mod greeting;fn main() {// 也会把 println! 的宏也给展开trace_macros!(true);let greet = greeting!("hbj", "rust");println!("{}", greet);let greet_with_default = greeting!("hbj");println!("{}", greet_with_default);// trace_macros!(false);}// cargo +nightly run
- 方式二:log_syntax! 宏,允许在编译期 log
pub fn base_greeting_fn(name: &str, greeting: &str) -> String {format!("{} {}!", greeting, name)}macro_rules! greeting {($name: literal) => {base_greeting_fn($name, "Hello")};($name: literal, $greeting: literal) => {base_greeting_fn($name, $greeting)};(test $name:literal) => {{log_syntax!("The name passed to test is {}", $name);println!("Returning default greeting");base_greeting_fn($name, "Hello")}};}
#![feature(trace_macros)]#![feature(log_syntax)]use crate::greeting::base_greeting_fn;#[macro_use]mod greeting;fn main() {trace_macros!(true);let _greet = greeting!("sam", "Heya");let _greet_with_default = greeting!("sam");let _greet_with_default_test = greeting!(test "Xiba");trace_macros!(false);}
声明宏有几种使用场景
- 避免重复和使用样板代码
- 做一些很难或不可能做的事情,比如默认参数、变长参数 或 DSL
