Rust进阶(15)-宏

1. 概述

  1. Rust中的宏主要有两种,一种是使用macro_rules!的声明宏;一种是过程宏。而过程宏又主要分为3种:
    1. 自定义宏#[derive],在结构体、枚举等上指定通过derive属性添加代码
    2. 类属性宏,定义可用于任意项的自定义属性
    3. 类函数宏,看起来像函数但是作用于作为参数传递的Token
  2. 宏和函数
    1. 宏是一种为写其它代码而写代码的方式。宏对于减少大量编写代码和维护代码非常有用
    2. 一个函数标签必须声明函数参数个数和类型,宏只接受可变参数
    3. 宏的定义比函数的定义更复杂
    4. 在调用宏之前,必须定义并将其引入作用域,而函数则可以在任何地方定义和调用
  3. #![xxx]表示应用于整个crate,#[]表示应用于紧接之后的代码块

2. 声明宏

匹配对应模式然后以另一部分代码替换当前代码

主目录:

1
2
3
4
5
6
7
8
9
10
11
cargo new mac --lib
cargo new main

# 主目录
vim Cargo.toml
# 加入如下内容:
[workspace]
members = [
"mac",
"main",
]

lib.rs中:

1
2
3
4
5
6
7
8
9
10
11
12
#[macro_export]
macro_rules! my_vec { //my_vec! 模仿vec!
($($x: expr), *) => {
{
let mut temp_vec = Vec::new();
$(
temp_vec.push($x);
)*
temp_vec
}
};
}

main.rs中:

1
2
3
4
5
6
7
8
9
10
11
use mac;
fn main() {
let v = mac::my_vec![1,2,3];
//mac::my_vec![1,2,3]等价于
let mut temp_vec = Vec::new();
temp_vec.push(1);
temp_vec.push(2);
temp_vec.push(3);
println!("v = {:?}", v);
println!("temp_vec = {:?}", temp_vec);
}

3. 自定义derive宏(过程宏)

  1. 过程宏接收Rust代码作为输入,在这些代码上进行操作,然后产生另一些代码作为输出,而非像声明宏那样匹配对应模式然后以另一部分代码替换当前代码
  2. 定义过程宏的函数接受一个TokenStream作为输入并产生一个TokenStream作为输出。这也就死活宏的核心:宏所处理的源代码组成了输入TokenStream,同时宏生成的代码是出入TokenStream。如下格式:
1
2
3
use proc_macro;
#[some_attribute]
pub fn some_name(input:TokenStream) -> TokenStream{}

项目主目录:

1
2
3
4
cargo new hello_macro --lib
cargo new main
# 进入heloo_macro目录,创建:
cargo new hello_macro_derive --lib

hello_macro的lib:

1
2
3
pub trait HelloMacro{
fn hello_macro();
}

hello_macro_derive中Cargo.toml关键配置:

1
2
3
4
5
6
[lib]
proc-macro = true

[dependencies]
syn = "0.14.4"
quote = "0.6.3"

hello_macro_derive中lib.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
extern crate proc_macro;
use crate::proc_macro::TokenStream;
use quote::quote;
use syn;

fn impl_hello_macro(ast: &syn::DeriveInput) -> TokenStream {
let name = &ast.ident;
let gen = quote! {
impl HelloMacro for #name{
fn hello_macro(){
println!("Hello,in my macro, my name is {}", stringify!(#name));
}
}
};
gen.into()
}

#[proc_macro_derive(HelloMacro)]
pub fn hello_macro_derive(input: TokenStream) -> TokenStream {
let ast = syn::parse(input).unwrap(); //D eriveInput
impl_hello_macro(&ast)
}

main的Cargo.toml中:

1
2
3
[dependencies]
hello_macro = {path = "../hello_macro"}
hello_macro_derive = {path = "../hello_macro/hello_macro_derive"}

main的main.rs中:

1
2
3
4
5
6
7
8
use hello_macro::HelloMacro;
use hello_macro_derive::HelloMacro;

#[derive(HelloMacro)]
struct Main;
fn main() {
Main::hello_macro();
}

4. 类属性宏和类函数宏(过程宏)

  1. 类属性宏
    1. 类属性宏与自定义派生宏相似,不同于为derive属性生成代码,它们允许你创建新的属性
    2. 类属性宏其它工作方式和自定义derive宏工作方式一致
  2. 类函数宏
    1. 类函数宏定义看起来像函数调用的宏,类似于macro_rules!,它们比函数更灵活

总结

本文编辑完毕

参考

[1] Rust 程序设计语言
[2] The Little Book of Rust Macros

  • Copyrights © 2017-2023 Jason
  • Visitors: | Views:

谢谢打赏~

微信