rust - macro_rules! 过程宏学习笔记 macro_rules!

rust中宏大致分两种:

  • 过程宏: 形如 println!(), vec!() 这类
  • 属性宏: 形如 #[derive(Debug)] 这种, 写在struct头上的
    其中过程宏定义起来比较简单, 使用方便,简洁

0x01 解读过程宏的定义

macro_rules! cc {
    () => {1+3};
}

如上所示, 这是一个比较简单的宏, 名称叫cc, 使用方法: cc!().
然后就会在编译出结果:4.
看一下上述代码结构, () => {1+3}; 其实对应的是一个模式匹配.本例中表示 无参数下, 会匹配到 1+3

再来一个例子:

macro_rules! times3 {
    ($e:expr) => {$e * 3};
    ($a:expr, $b:expr, $c:expr)=> {$a * ($b + $c)};
}

这个例子有两个模式匹配, 第一个包含一个参数, 第二个模式包含三个参数, 理解起来也很简单.
需要说明的是 参数的类型, 大致分以下几种, 上面使用比较常见的类型 expr: 即表达式

 item :例如 函数、结构、模块等等
 block : 代码块(例如 表达式或者复制代码块,用花括号包起来)
 stmt:赋值语句(statement)
 pat :Pattern ,匹配
 expr :表达式,expression  : 1 + 2
 ty:类型
 ident:标记,识别码
 path:路径(例如:foo, ::std::men::replace,transmute::<_,int>,...)
 meta:元项目;在 #[...] 和 #![...] 属性里面的内容
 tt:单 token tree : 1, 2

0x02 多参数匹配

类似于java中的 arg..., 过程宏定义中, 也有相应的写法, 来看个例子:

macro_rules! rep {
    () => {-1};
    ($ ($e:expr) ,+) => {
        {
            let mut v = Vec::new();
            $(
                v.push($e);
            )+
            v
        }
    };
    ($ ($e:expr) +) => {
        {
            let mut sum = 0;
            $(
                sum = sum + $e;
            )+
            sum
        }
    };
}

/// 使用例子:
    println!("rep no param {}", rep!());
    println!("rep has params {:?}", rep![1, 2, 3]);
    println!("rep sum params {:?}", rep!(1 2 3 4 5));

本例包含三种参数方式:

  • 无参, 上面已经解释过了.
  • 以逗号分隔的参数串
  • 以 空格分隔的参数串
    需要注意的是, 参数匹配, 和 值的使用是一致的, 都采用 $( ... )+ 写法进行套用即可.

0x03 关于类型 tt

macro_rules! param_count {
    ($a:tt + $b:tt) => {"got an  a+b expression"};
    ($i:ident)=>{"got an identifier"};
    ($a:tt kiss $b:tt) => {$a + $b};
    ($($e:tt)*)=>{"got some tokens"};
}

    println!("param_cnt 1: {}", param_count!(3+4));
    println!("param_cnt 2: {}", param_count!(a));
    println!("param_cnt 3: {}", param_count!(4 5 6));
    println!("param_cnt 4: {}", param_count!(7 kiss 8));

--- 结果
param_cnt 1: got an  a+b expression
param_cnt 2: got an identifier
param_cnt 3: got some tokens
param_cnt 4: 15

可以看到, 在宏的参数里, 可以写非关键字的任意字符, 这个可用于自定义 DSL

0x04 综合使用: 自定义 struct 模板

macro_rules! my_cc {
    (
        struct $name:ident {
             $(pub $field_name:ident: $field_type:ty,)*
        }
    )
 => {
        struct $name {
            $($field_name: $field_type,)*
        }

        impl $name {

            fn log(self) {
                $( println!("{} -> {:?}", stringify!($field_name), self.$field_name); )*
            }
        }
    }
}

my_cc!(struct Hello {
    pub name:String,
    pub size:String,
});

let t = Hello { name: String::from("gorey"), size: String::from("18") };
t.log();

---- 结果

name -> "gorey"
size -> "18"

通过 my_cc!的方法来定义一个struct, 同时自动实现了可以打印参数名/值的日志方法.
后续, 可以扩展成用于值校验.

写在最后, 本篇的扩展实用文: rust-参数校验宏实现

你可能感兴趣的:(rust - macro_rules! 过程宏学习笔记 macro_rules!)