Bootstrap

rust学习-宏的定义与使用

在 Rust 中,宏(macro)是一种在编译期间生成代码的机制,宏可以以更加灵活和高效的方式编写代码,Rust中有两种主要的宏:声明宏(macro_rules! 宏)和过程宏(procedural macros)

声明宏(macro_rules! 宏)

声明宏是使用 macro_rules! 关键字定义:

macro_rules! 宏名 {
    ($($pattern:pat) => $body:expr);
    ($($pattern:pat) => $body:expr);
    // 更多模式...
}

使用方式

1. 简单的宏

示例一个用于打印消息的宏:

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

fn main() {
    say_hello!();
}

2. 带参数的宏

定义一个带参数的宏,用于打印带参数的消息:

macro_rules! say_hello_to {
    ($name:expr) => {
        println!("Hello, {}!", $name);
    };
}

fn main() {
    say_hello_to!("XiaoMing");
}

3. 多个模式的宏

定义一个宏,支持多种模式:

macro_rules! math {
    ($x:expr, +, $y:expr) => {
        $x + $y
    };
    ($x:expr, -, $y:expr) => {
        $x - $y
    };
}

fn main() {
    let result1 = math!(10, +, 5);
    let result2 = math!(10, -, 5);
    
    println!("10 + 5 = {}", result1); // 输出: 10 + 5 = 15
    println!("10 - 5 = {}", result2); // 输出: 10 - 5 = 5
}

过程宏

1. 定义过程宏

过程宏需要一个特殊的 crate 类型,并且需要使用 proc_macro 库,常见的过程宏有:属性宏(attribute-like macros)、函数宏(function-like macros)和派生宏(derive macros)

1.1 属性宏

属性宏用于在代码上添加属性,通常用于生成额外的代码或修改现有的代码

use proc_macro;
use proc_macro2::TokenStream;
use quote::quote;
use syn;

#[proc_macro_attribute]
pub fn my_attribute(_attr: TokenStream, item: TokenStream) -> TokenStream {
    let ast = syn::parse_macro_input!(item as syn::ItemFn);
    let ident = &ast.sig.ident;

    let expanded = quote! {
        fn #ident() {
            println!("Before function call");
            #ast
            println!("After function call");
        }
    };

    expanded.into()
}

1.2 函数宏

函数宏类似于传统的宏,但可以在编译时生成更复杂的代码

extern crate proc_macro;

use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, ItemFn};

#[proc_macro]
pub fn my_function(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as ItemFn);
    let name = &input.sig.ident;

    let expanded = quote! {
        fn #name() {
            println!("This is a generated function");
        }
    };

    expanded.into()
}

1.3 派生宏

派生宏用于为结构体或枚举生成派生的实现

extern crate proc_macro;

use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, DeriveInput};

#[proc_macro_derive(MyTrait)]
pub fn my_trait_derive(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as DeriveInput);
    let name = &input.ident;

    let expanded = quote! {
        impl MyTrait for #name {
            fn my_method(&self) {
                println!("This is a derived method for {}", stringify!(#name));
            }
        }
    };

    expanded.into()
}

2. 使用过程宏

使用过程宏需要在 Cargo.toml 中声明依赖,并在代码中使用宏

2.1 属性宏

#[my_attribute]
fn my_function() {
    println!("Inside my function");
}

fn main() {
    my_function();
}

2.2 函数宏

my_function! {
    fn my_generated_function() {
        println!("Inside generated function");
    }
}

fn main() {
    my_generated_function();
}

2.3 派生宏

#[derive(MyTrait)]
struct MyStruct;

trait MyTrait {
    fn my_method(&self);
}

fn main() {
    let my_struct = MyStruct;
    my_struct.my_method();
}
;