基本使用

  1. 可以选择括号:( ){ }[ ],随便选
  2. 格式:(matcher) => (transcriber)

声明宏 Declarative macros - 图1

  1. // 声明一个新的宏,命名为 my_vec
  2. macro_rules! my_vec {
  3. // () is our first matcher
  4. // 由于它是空的,它将匹配不带任何参数的宏调用
  5. () => [ Vec::new() ];
  6. // (make an empty vec) is our second matcher
  7. // 当我们的输入字面上 “make a empty vec” 时,它就会匹配
  8. (make an empty vec) => ( Vec::new() );
  9. // 接受一个表达式 (expr) 并将其绑定到 x
  10. { $x:expr } => {
  11. {
  12. let mut vec = Vec::new();
  13. vec.push($x);
  14. vec
  15. }
  16. };
  17. // 接受多个用逗号分隔的表达式,同样的将它们绑定到 x
  18. [ $($x:expr),* ] => (
  19. {
  20. let mut vec = Vec::new();
  21. $(vec.push($x);)*
  22. vec
  23. }
  24. );
  25. }
  1. #[macro_use]
  2. mod my_vec;
  3. fn main() {
  4. let empty: Vec<i32> = my_vec![];
  5. println!("Empty vector: {:?}", empty);
  6. let also_empty: Vec<&str> = my_vec!("make an empty vec");
  7. println!("also_empty vector: {:?}", also_empty);
  8. let three_numbers: Vec<i32> = my_vec!(1, 2, 3);
  9. println!("three_numbers: {:?}", three_numbers);
  10. }

也可以写在同一个文件中,但宏要标明导出,如下所示

  1. fn main() {
  2. let empty: Vec<i32> = my_vec![];
  3. println!("Empty vector: {:?}", empty);
  4. let also_empty: Vec<&str> = my_vec!("make an empty vec");
  5. println!("also_empty vector: {:?}", also_empty);
  6. let three_numbers: Vec<i32> = my_vec!(1, 2, 3);
  7. println!("three_numbers: {:?}", three_numbers);
  8. }
  9. // 声明一个新的宏,命名为 my_vec
  10. #[macro_export]
  11. macro_rules! my_vec {
  12. // () is our first matcher
  13. // 由于它是空的,它将匹配不带任何参数的宏调用
  14. () => [ Vec::new() ];
  15. // (make an empty vec) is our second matcher
  16. // 当我们的输入字面上 “make a empty vec” 时,它就会匹配
  17. (make an empty vec) => ( Vec::new() );
  18. // 接受一个表达式 (expr) 并将其绑定到 x
  19. { $x:expr } => {
  20. {
  21. let mut vec = Vec::new();
  22. vec.push($x);
  23. vec
  24. }
  25. };
  26. // 接受多个用逗号分隔的表达式,同样的将它们绑定到 x
  27. [ $($x:expr),* ] => (
  28. {
  29. let mut vec = Vec::new();
  30. $(vec.push($x);)*
  31. vec
  32. }
  33. );
  34. }

可变参数和默认参数

Rust 函数不允许可变参数。一个原因可能是可变参数让编译器的生活变得更加困难。或者,它可能不是一个足够重要的功能。可以用宏实现

声明宏 Declarative macros - 图2

  1. pub fn base_greeting_fn(name: &str, greeting: &str) -> String {
  2. format!("{} {}!", greeting, name)
  3. }
  4. macro_rules! greeting {
  5. ($name: literal) => {
  6. base_greeting_fn($name, "Hello")
  7. };
  8. ($name: literal, $greeting: literal) => {
  9. base_greeting_fn($name, $greeting)
  10. };
  11. }
  1. // 没有这个 use,宏那边的文件会报找不到 base_greeting_fn 函数的错误
  2. use crate::greeting::base_greeting_fn;
  3. #[macro_use]
  4. mod greeting;
  5. fn main() {
  6. let greet = greeting!("hbj", "rust");
  7. println!("{}", greet);
  8. let greet_with_default = greeting!("hbj");
  9. println!("{}", greet_with_default);
  10. }

展开宏代码的方式

  1. 方式一:使用 宏追踪(trace macros)**#![feature(trace_macros)]**
  1. // rustup default nightly 或者 cargo +nightly your-command
  2. #![feature(trace_macros)]
  3. use crate::greeting::base_greeting_fn;
  4. #[macro_use]
  5. mod greeting;
  6. fn main() {
  7. // 也会把 println! 的宏也给展开
  8. trace_macros!(true);
  9. let greet = greeting!("hbj", "rust");
  10. println!("{}", greet);
  11. let greet_with_default = greeting!("hbj");
  12. println!("{}", greet_with_default);
  13. // trace_macros!(false);
  14. }
  15. // cargo +nightly run
  1. 方式二log_syntax! 宏,允许在编译期 log
  1. pub fn base_greeting_fn(name: &str, greeting: &str) -> String {
  2. format!("{} {}!", greeting, name)
  3. }
  4. macro_rules! greeting {
  5. ($name: literal) => {
  6. base_greeting_fn($name, "Hello")
  7. };
  8. ($name: literal, $greeting: literal) => {
  9. base_greeting_fn($name, $greeting)
  10. };
  11. (test $name:literal) => {{
  12. log_syntax!("The name passed to test is {}", $name);
  13. println!("Returning default greeting");
  14. base_greeting_fn($name, "Hello")
  15. }};
  16. }
  1. #![feature(trace_macros)]
  2. #![feature(log_syntax)]
  3. use crate::greeting::base_greeting_fn;
  4. #[macro_use]
  5. mod greeting;
  6. fn main() {
  7. trace_macros!(true);
  8. let _greet = greeting!("sam", "Heya");
  9. let _greet_with_default = greeting!("sam");
  10. let _greet_with_default_test = greeting!(test "Xiba");
  11. trace_macros!(false);
  12. }

声明宏有几种使用场景

  1. 避免重复和使用样板代码
  2. 做一些很难或不可能做的事情,比如默认参数变长参数 DSL