Rust 的宏

宏的种类

Rust 的宏系统是其强大功能之一,允许你在编译时生成代码。Rust 有两种主要类型的宏:

  1. 声明式宏(Declarative Macros) - 使用 macro_rules! 语法
  2. 过程宏(Procedural Macros) - 更强大也更复杂,分为三种:
    • 自定义派生(#[derive] 宏)
    • 属性式宏
    • 函数式宏

声明式宏

使用 macro_rules! 定义。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
macro_rules! my_vec {
    // 空向量
    () => {
        Vec::new()
    };
    // 带初始值的向量
    ($($x:expr),+ $(,)?) => {
        {
            let mut temp_vec = Vec::new();
            $(
                temp_vec.push($x);
            )+
            temp_vec
        }
    };
}

fn main() {
    let v1: Vec<i32> = my_vec!();
    let v2 = my_vec!(1, 2, 3);
    println!("{:?}, {:?}", v1, v2); // [], [1, 2, 3]
}

模式匹配中的元变量:

  • $x:expr - 匹配表达式
  • $x:ty - 匹配类型
  • $x:ident - 匹配标识符(变量名、函数名等)
  • $x:item - 匹配项(函数、结构体等)
  • $x:block - 匹配代码块

See 指示符

过程宏

自定义派生宏

自定义派生宏可以使用 derive 属性派生。

定义 (放到一个 lib crate):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, DeriveInput};

// 定义 trait 让用户实现
#[proc_macro_derive(HelloMacro)]
pub fn hello_macro_derive(input: TokenStream) -> TokenStream {
    // 解析输入为 DeriveInput 语法树
    let input = parse_macro_input!(input as DeriveInput);
    let name = input.ident;
    
    // 使用 quote! 宏生成代码
    let expanded = quote! {
        // 为类型实现 HelloMacro trait
        impl HelloMacro for #name {
            fn hello_macro() {
                println!("Hello, Macro! My name is {}!", stringify!(#name));
            }
        }
    };
    
    // 将生成的代码转换回 TokenStream
    TokenStream::from(expanded)
}

使用:

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

#[derive(HelloMacro)]
struct Pancakes;

fn main() {
    Pancakes::hello_macro(); // 输出: Hello, Macro! My name is Pancakes!
}

属性式宏

定义 (放到一个 lib crate):

 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
31
32
33
34
35
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, AttributeArgs, ItemFn, NestedMeta, Lit};

#[proc_macro_attribute]
pub fn route(attr: TokenStream, item: TokenStream) -> TokenStream {
    // 解析属性参数
    let args = parse_macro_input!(attr as AttributeArgs);
    // 解析被修饰的函数
    let input = parse_macro_input!(item as ItemFn);
    
    // 提取HTTP方法和路径
    let http_method = match &args[0] {
        NestedMeta::Meta(meta) => meta.path().segments[0].ident.to_string(),
        _ => panic!("Expected HTTP method"),
    };
    
    let path = match &args[1] {
        NestedMeta::Lit(Lit::Str(lit)) => lit.value(),
        _ => panic!("Expected path string"),
    };
    
    let fn_name = &input.sig.ident;
    let fn_block = &input.block;
    
    // 生成代码
    let expanded = quote! {
        fn #fn_name() {
            println!("注册路由: {} {}, 处理函数: {}", #http_method, #path, stringify!(#fn_name));
            #fn_block
        }
    };
    
    TokenStream::from(expanded)
}

使用:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
use my_attribute_macro::route;

#[route(GET, "/")]
fn index() {
    println!("处理首页请求");
}

#[route(POST, "/users")]
fn create_user() {
    println!("创建新用户");
}

fn main() {
    index();
    create_user();
}

输出结果:

1
2
3
4
注册路由: GET /, 处理函数: index
处理首页请求
注册路由: POST /users, 处理函数: create_user
创建新用户

函数式宏

定义 (放到一个 lib crate):

 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
31
32
33
34
35
use proc_macro::TokenStream;
use quote::quote;
use syn::parse_macro_input;

#[proc_macro]
pub fn sql(input: TokenStream) -> TokenStream {
    // 解析输入
    let input = parse_macro_input!(input as syn::Expr);
    
    // 生成代码
    let expanded = quote! {
        {
            println!("准备执行SQL: {}", stringify!(#input));
            // 这里可以添加实际的SQL执行逻辑
            #input.to_string()
        }
    };
    
    TokenStream::from(expanded)
}

#[proc_macro]
pub fn vec_string(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as syn::ExprArray);
    
    let elements = input.elems.iter().map(|e| {
        quote! { #e.to_string() }
    });
    
    let expanded = quote! {
        vec![ #(#elements),* ]
    };
    
    TokenStream::from(expanded)
}

使用:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
use my_function_macro::{sql, vec_string};

fn main() {
    // 使用sql!宏
    let query = sql!(SELECT * FROM users WHERE id = 1);
    println!("生成的查询: {}", query);
    
    // 使用vec_string!宏
    let strings = vec_string!["hello", 42, true, 3.14];
    println!("字符串向量: {:?}", strings);
    
    // 更复杂的SQL示例
    let user_id = 10;
    let query2 = sql! {
        SELECT name, email FROM users 
        WHERE id = #user_id AND active = true
    };
    println!("带参数的查询: {}", query2);
}

输出结果:

1
2
3
4
5
准备执行SQL: SELECT * FROM users WHERE id = 1
生成的查询: SELECT * FROM users WHERE id = 1
字符串向量: ["hello", "42", "true", "3.14"]
准备执行SQL: SELECT name, email FROM users WHERE id = # user_id AND active = true
带参数的查询: SELECT name, email FROM users WHERE id = 10 AND active = true
0%