MST

星途 面试题库

面试题:Rust宏定义之基础使用

请简述Rust中宏定义的两种主要类型(声明式宏和过程宏)的区别,并分别举例说明如何定义和使用它们。在使用声明式宏时,模式匹配的规则是怎样的?
10.4万 热度难度
编程语言Rust

知识考点

AI 面试

面试题答案

一键面试

声明式宏和过程宏的区别

  1. 声明式宏(Macro by example)
    • 定义:通过模式匹配来展开代码。它基于macro_rules!关键字定义,通常用于生成重复的代码结构。
    • 适用场景:适用于简单的代码生成和替换,比如生成相同结构的代码片段。
  2. 过程宏(Procedural macro)
    • 定义:以函数的方式定义,接受TokenStream作为输入并返回处理后的TokenStream。有三种类型:函数式宏(#[proc_macro])、类属性宏(#[proc_macro_attribute])和类推导宏(#[proc_macro_derive])。
    • 适用场景:适用于更复杂的代码生成和元编程,例如根据结构体定义自动生成一些方法等。

声明式宏定义和使用示例

// 定义声明式宏
macro_rules! print_vec {
    ($($x:expr),*) => {
        {
            let v: Vec<&str> = vec![$(stringify!($x)),*];
            println!("{:?}", v);
        }
    };
}

fn main() {
    // 使用声明式宏
    print_vec!(hello, world);
}

过程宏(函数式宏)定义和使用示例

  1. 定义一个函数式宏
    • 创建一个新的Crate,比如my_proc_macro,在Cargo.toml中添加如下内容:
    [lib]
    proc-macro = true
    
    • src/lib.rs中编写代码:
    use proc_macro::TokenStream;
    #[proc_macro]
    pub fn my_macro(input: TokenStream) -> TokenStream {
        input.into_iter().filter(|token| token.to_string()!= " ").collect()
    }
    
  2. 使用函数式宏
    • 在另一个Crate中,在Cargo.toml中添加依赖:
    my_proc_macro = { path = "../my_proc_macro" }
    
    • src/main.rs中使用:
    extern crate my_proc_macro;
    my_proc_macro!(println!("Hello, World!"));
    

声明式宏模式匹配规则

  1. 通配符模式
    • $():用于匹配零个或多个项。例如在print_vec宏中($($x:expr),*)$x是一个模式变量,*表示匹配零个或多个表达式,并且每个表达式用逗号分隔。
  2. 模式变量
    • 模式变量的语法是$name:tt形式,其中name是变量名,tt是标记树(token tree)类型。常见的类型有expr(表达式)、stmt(语句)、ident(标识符)等。例如$x:expr表示匹配一个表达式并将其绑定到$x变量。
  3. 重复模式
    • 除了*(零个或多个),还可以用+(一个或多个)和?(零个或一个)。例如($($x:expr),+)表示匹配一个或多个用逗号分隔的表达式。