宏编程

声明宏(Declarative Macros)是 Rust 中的一种宏,用于通过模式匹配和替换来生成代码。声明宏使用 macro_rules! 关键字定义,适合用于生成重复的代码模式,简化代码编写,减少样板代码。

基本概念

声明宏通过匹配输入的模式来生成相应的代码。它们在编译时展开,允许你在编译时生成复杂的代码结构。

示例

以下是一个简单的声明宏示例,展示了如何定义和使用声明宏:

定义宏

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// 定义一个名为 `vec` 的宏,用于创建一个包含多个元素的 Vec
macro_rules! vec {
// 匹配模式,用于匹配单个表达式,并将其包裹在 Vec 中
($elem:expr) => {
{
let mut v = Vec::new();
v.push($elem);
v
}
};
// 匹配模式,用于匹配多个表达式,并将它们包裹在 Vec 中
($($elem:expr),*) => {
{
let mut v = Vec::new();
$(
v.push($elem);
)*
v
}
};
}

fn main() {
// 使用宏创建 Vec
let v1: Vec<i32> = vec![1];
let v2: Vec<i32> = vec![1, 2, 3];

println!("{:?}", v1); // 输出: [1]
println!("{:?}", v2); // 输出: [1, 2, 3]
}

解释

  1. 定义宏

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    macro_rules! vec {
    // 匹配单个表达式
    ($elem:expr) => {
    {
    let mut v = Vec::new();
    v.push($elem);
    v
    }
    };
    // 匹配多个表达式
    ($($elem:expr),*) => {
    {
    let mut v = Vec::new();
    $(
    v.push($elem);
    )*
    v
    }
    };
    }
    • macro_rules! vec 定义了一个名为 vec 的宏。
    • $elem:expr 匹配一个表达式。
    • $($elem:expr),* 匹配零个或多个表达式,使用逗号分隔。
  2. 使用宏

    1
    2
    let v1: Vec<i32> = vec![1];
    let v2: Vec<i32> = vec![1, 2, 3];
    • vec![1] 使用宏创建一个包含单个元素的 Vec
    • vec![1, 2, 3] 使用宏创建一个包含多个元素的 Vec

优势

  • 减少样板代码:声明宏可以减少重复的样板代码,使代码更简洁。
  • 提高代码可读性:通过定义宏,可以使代码更具可读性和可维护性。
  • 灵活性:声明宏可以匹配不同的输入模式,生成不同的代码。

使用场景

  • 创建集合(如 VecHashMap)的便捷宏。
  • 生成重复的代码模式。
  • 编写自定义的 DSL(领域特定语言)。

proc_macro_derive 是 Rust 中的一种过程宏,用于为用户定义的类型自动生成代码。它允许你定义自定义派生(derive)属性,这样你可以为你的数据结构自动生成实现特定特性(traits)的代码。通过使用 proc_macro_derive,你可以减少样板代码的编写,提高代码的可维护性和可读性。

基本概念

  • 过程宏(Procedural Macros):过程宏是一种用于在编译时生成代码的宏。与属性宏和函数宏不同,过程宏可以操作整个语法树。
  • 派生宏(Derive Macros):派生宏是一种过程宏,用于为类型自动生成实现特定特性的代码。它们通常用于自动生成 DebugCloneSerialize 等特性的实现。

示例

以下是一个简单的示例,展示了如何定义和使用 proc_macro_derive 来为一个结构体自动生成 HelloWorld 特性的实现。

步骤 1:创建一个新的 Rust 项目

1
2
cargo new my_macro --lib
cd my_macro

步骤 2:在 Cargo.toml 中添加依赖

Cargo.toml 中添加 proc-macro 特性:

1
2
[lib]
proc-macro = true

步骤 3:编写过程宏

src/lib.rs 文件中,定义一个自定义派生宏 HelloWorld

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

#[proc_macro_derive(HelloWorld)]
pub fn hello_world_derive(input: TokenStream) -> TokenStream {
// 解析输入的 Rust 代码为语法树
let ast = syn::parse(input).unwrap();

// 构建新的代码
impl_hello_world(&ast)
}

fn impl_hello_world(ast: &syn::DeriveInput) -> TokenStream {
let name = &ast.ident;
let gen = quote! {
impl HelloWorld for #name {
fn hello_world() {
println!("Hello, World! I am a {}!", stringify!(#name));
}
}
};
gen.into()
}

步骤 4:在主项目中使用过程宏

创建一个新的二进制项目以使用该宏:

1
2
cargo new my_project
cd my_project

my_projectCargo.toml 中添加对 my_macro 的依赖:

1
2
[dependencies]
my_macro = { path = "../my_macro" }

src/main.rs 中使用自定义派生宏:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 引入自定义宏
use my_macro::HelloWorld;

// 定义一个结构体
#[derive(HelloWorld)]
struct MyStruct;

// 定义 HelloWorld 特性
trait HelloWorld {
fn hello_world();
}

fn main() {
MyStruct::hello_world(); // 输出: Hello, World! I am a MyStruct!
}

解释

  1. 创建过程宏

    • #[proc_macro_derive(HelloWorld)]:定义一个名为 HelloWorld 的派生宏。
    • fn hello_world_derive(input: TokenStream) -> TokenStream:这个函数解析输入的 Rust 代码并生成新的代码。
    • syn::parse(input).unwrap():使用 syn 库解析输入的代码为语法树。
    • quote!:使用 quote 库生成新的代码。
  2. 使用过程宏

    • 在你的项目中,使用 #[derive(HelloWorld)] 为结构体自动生成 HelloWorld 特性的实现。
    • 定义 HelloWorld 特性,并调用自动生成的方法。

宏编程
https://abrance.github.io/2025/01/10/mdstorage/domain/rust/宏编程/
Author
xiaoy
Posted on
January 10, 2025
Licensed under