第九章 Rust 宏 Macros

宏允许开发者在编译时生成代码,从而减少重复工作并提升灵活性。

Rust 的宏分为两类:

  • 声明式宏(Declarative Macros):通过 macro_rules! 定义,基于模式匹配和文本替换,简单易用但功能有限。
  • 过程宏(Procedural Macros):通过解析和修改 AST(抽象语法树)生成代码,包括派生宏、属性式宏和函数式宏,更强大但实现复杂。

一、常见的宏

fn main() {
    // println! - 输出文本
    println!("Processing data...");

    // vec! - 创建动态数组
    let numbers = vec![10, 20, 30];

    // assert! - 验证条件
    let value = 5;
    assert!(value > 0, "Value must be positive!");

    // format! - 生成格式化字符串
    let text = format!("Found {} items", numbers.len());
    println!("{}", text);
}

二、声明式宏

基础语法

// 多模式声明式宏
macro_rules! log_items {
    // 匹配模式1
    ($item:expr, $count:expr) => {{
        for _ in 0..$count {
            println!("Item: {}", $item);
        }
    }};
    // 匹配模式2
    ($item:expr) => {{
        println!("Single item: {}", $item);
    }};
}

fn main() {
    // 根据参数数量自动选择匹配的模式
    log_items!("box", 2);
    log_items!("crate");
    // 输出:
    // Item: box
    // Item: box
    // Single item: crate
}

说明:

  • $item:expr 匹配一个表达式(如"box"),$count:expr 匹配次数,宏展开为一个 for 循环,直接嵌入代码。
  • 注意 specifier (如上的 expr)不能随便写,rust 提供了一系列合法的 specifier,需要根据需求按需选择

可变参数宏

macro_rules! sum_values {
    // $($value:expr),* 表示零个或多个表达式,展开时为每个值生成 total += $value。这使得宏能处理任意数量的输入。
    ($($value:expr),*) => {{
        let mut total = 0;
        $(
            total += $value;
        )*
        total
    }};
}

fn main() {
    let result = sum_values!(1, 2, 3, 4);
    println!("Sum: {}", result); // 输出: Sum: 10
}

三、过程宏

派生宏为类型自动实现特征

派生宏

#[derive(Debug)]
struct Record {
    id: u32,
    name: String,
}

fn main() {
    let record = Record {
        id: 1,
        name: String::from("Item A"),
    };
    println!("{:?}", record); // 输出: Record { id: 1, name: "Item A" }
}

#[derive(Debug)] 在编译时为 Record 追加 Debug 特征的实现。

自定义派生宏代码模板:

extern crate proc_macro;
use proc_macro::TokenStream;
use quote::quote;
use syn;

// 自定义派生宏;quote! 生成特征实现代码,#name 插入类型名称
#[proc_macro_derive(Report)]
pub fn report_derive(input: TokenStream) -> TokenStream {
    let ast = syn::parse(input).unwrap();
    let name = &ast.ident;
    let gen = quote! {
        impl Report for #name {
            fn report() {
                println!("Reporting: {}", stringify!(#name));
            }
        }
    };
    gen.into()
}

// 使用示例
#[derive(Report)]
struct Box;

#[derive(Report)]
struct Tray;

fn main() {
    Box::report();  // 输出: Reporting: Box
    Tray::report(); // 输出: Reporting: Tray
}

属性式宏

属性式宏通过 #[attr] 为代码添加自定义属性,扩展其功能:

#[proc_macro_attribute]
pub fn track_call(attr: TokenStream, item: TokenStream) -> TokenStream {
    let item_ast = syn::parse::<syn::ItemFn>(item.clone()).unwrap();
    let fn_name = &item_ast.sig.ident;
    let gen = quote! {
        #item_ast
        fn track_wrapper() {
            println!("Calling function: {}", stringify!(#fn_name));
            // 调用 process_data()
            #fn_name();
        }
    };
    gen.into()
}

// 使用示例
#[track_call]
fn process_data() {
    println!("Data processed.");
}

fn main() {
    track_wrapper(); // 输出: Calling function: process_data \n Data processed.
}

函数式宏

函数式宏通过 name!(...) 调用,像函数但在编译时生成代码,类似 println!(...)

#[proc_macro]
pub fn log_event(input: TokenStream) -> TokenStream {
    let input_str = input.to_string();
    let gen = quote! {
        println!("Event logged: {}", #input_str);
    };
    gen.into()
}

// 使用示例
fn main() {
    log_event!("system start");
    // 输出: Event logged: system start
}

宏与函数

fn log_once() {
    println!("Logged!");
}

macro_rules! log_once {
    () => {
        println!("Logged!");
    };
}

fn main() {
    log_once();  // 函数调用
    log_once!(); // 宏展开
}
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容