基本使用
- 可以选择括号:( )、{ }、[ ],随便选
- 格式:(matcher) => (transcriber)
// 声明一个新的宏,命名为 my_vec
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
}
);
}
#[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