17 rust基础 - 宏 macro

📌 Rust 宏(Macro)

Rust 中的 宏(macro) 是一种 元编程 机制,允许编写可扩展、动态生成的代码。相比于函数,宏能够:

  • 在编译时展开,减少运行时开销
  • 接受不同数量的参数,不像函数必须指定固定的参数
  • 操作 Rust 代码结构,可以生成代码块、表达式、类型等

🌟 Rust 宏的四种类型

Rust 提供了 4 种主要的宏:

宏类型 用途
声明式宏(macro_rules! 最常用的宏,用于模式匹配、代码展开
过程宏(Procedural Macros) 处理 Rust 代码的自定义宏
派生宏(Derive Macros) 通过 #[derive] 自动生成 trait 实现
属性宏(Attribute Macros) 自定义属性,影响编译行为

🟢 1. 声明式宏(macro_rules!

声明式宏 使用 macro_rules! 定义,类似于 模式匹配,可以匹配不同的参数模式并展开代码。

📌 1.1 例子:最简单的宏

macro_rules! say_hello {
    () => {
        println!("Hello, Rust macros!");
    };
}

fn main() {
    say_hello!(); // 调用宏(类似函数,但不用 `()`)
}

📌 1.2 例子:带参数的宏

macro_rules! print_value {
    ($val:expr) => {
        println!("Value: {}", $val);
    };
}

fn main() {
    print_value!(42);
    print_value!("Rust is awesome!");
}
  • $val:expr:表示参数 $val 是一个表达式
  • println!() 也是一个宏,它接受不同类型的输入

📌 1.3 例子:匹配多个模式

宏可以有多个分支:

macro_rules! my_macro {
    () => {
        println!("No arguments!");
    };
    ($val:expr) => {
        println!("One argument: {}", $val);
    };
    ($a:expr, $b:expr) => {
        println!("Two arguments: {}, {}", $a, $b);
    };
}

fn main() {
    my_macro!();
    my_macro!(10);
    my_macro!("Hello", 20);
}

🔹 编译器会自动选择匹配的模式


📌 1.4 例子:重复匹配 ($()*)

可以匹配 可变数量 的参数:

macro_rules! sum {
    ($($num:expr),*) => {
        {
            let mut total = 0;
            $(total += $num;)*
            total
        }
    };
}

fn main() {
    let result = sum!(1, 2, 3, 4, 5);
    println!("Sum: {}", result);
}

🔹 $()* 代表 可重复匹配多个参数,然后展开代码。


🟠 2. 过程宏(Procedural Macros)

过程宏 更强大,适用于 代码转换。主要有三种:

  1. 派生宏(Derive Macros)
  2. 函数式宏(Function-like Macros)
  3. 属性宏(Attribute Macros)

📌 2.1 派生宏(#[derive]

派生宏用于自动实现 trait

use serde::{Serialize, Deserialize};

#[derive(Debug, Serialize, Deserialize)]
struct User {
    name: String,
    age: u8,
}

fn main() {
    let user = User { name: "Alice".to_string(), age: 25 };
    println!("{:?}", user);
}

🔹 #[derive(Debug)] 生成 Debug 实现,方便 println!("{:?}") 打印结构体信息。


📌 2.2 自定义过程宏

需要在 独立的 proc-macro crate 里实现:

use proc_macro::TokenStream;

#[proc_macro]
pub fn my_macro(input: TokenStream) -> TokenStream {
    println!("Macro input: {}", input);
    input
}

🔹 过程宏可以操作 AST(抽象语法树),比 macro_rules! 更灵活。


🔴 3. 宏 VS 函数

特点 函数
代码展开方式 运行时调用 编译时展开(零成本)
类型检查 需要固定参数类型 可以适配不同类型
灵活性 只能有固定参数 可变参数数量,模式匹配
语法复杂度 简单 较难调试

🟣 4. 宏的常见问题

❌ 1. 宏定义范围

mod macros {
    macro_rules! my_macro {
        () => {
            println!("Hello from macro!");
        };
    }
}

fn main() {
    my_macro!(); // ❌ 错误:宏 `my_macro!` 在 `mod macros` 里,无法直接使用
}

✅ 解决方法:使用 #[macro_export]

#[macro_export]
macro_rules! my_macro {
    () => {
        println!("Hello from macro!");
    };
}

❌ 2. 传递 tt(Token Tree)

Rust 宏 不能直接传递 iflet 等关键字

macro_rules! bad_macro {
    ($keyword:tt) => { // ❌ `tt` 匹配不了 Rust 关键字
        $keyword println!("This won't work!");
    };
}

fn main() {
    bad_macro!(if); // ❌ 编译错误
}

✅ 解决方法:直接传入完整代码块:

macro_rules! good_macro {
    ($block:block) => {
        $block
    };
}

fn main() {
    good_macro!({
        if true {
            println!("Now it works!");
        }
    });
}

🌟 总结

概念 说明
声明式宏(macro_rules! 基于 模式匹配 的宏,最常用
过程宏(Procedural Macros) 用于 AST 代码转换
派生宏(Derive Macros) #[derive(Debug)] 自动生成 trait 实现
属性宏(Attribute Macros) #[my_macro] 影响编译
可变参数宏 $()* 匹配多个参数
tt(Token Tree) 可以匹配任何 Rust 代码
macro_export 让宏在其他模块可见

Rust 宏很强大,但 调试比较困难,一般用于:

  • 代码重复性高的场景
  • 性能要求极高的代码
  • 框架开发(如 serde, tokio
posted @ 2025-03-23 21:46  代码世界faq  阅读(26)  评论(0)    收藏  举报