用声明式宏 declarative macro 解析 Rust 语法

在上一篇 Rust 声明式宏中的 Metavariables 有哪些 的基础上,
今天尝试解析一下 Rust 中的几种 item。我们知道一个 crate 是由 item 组成的,每一个 fn struct enum impl mod 等定义都是一个 item,
这篇文章就简单解析一下 Functionstruct

Function

先看一个最简单的函数

fn foo() {}

这个 foo 函数由关键字 fn 开头,后面跟一个函数名($function_name: ident), 然后是一对 (), 再跟一个函数体 block

macro_rules! function_item_matcher {
    (fn $name: ident () $block: block) => {
        fn $name() $block
    };
}

function_item_matcher! {
    fn hello(){
        println!("hello");
    }
}

再复杂一点,给我们的 foo 函数加点料

#[allow(unused_variables)]
pub async fn foo(arg1: u8) -> u8 { arg1 }

我们的 foo 函数已经具备了常见函数的基本形态,除了没有泛型等比较复杂的部分,这里了解分析方法就行,有需要的话再继续抽丝剥茧即可。
完整的 Function 的语法定义看这里: https://doc.rust-lang.org/reference/items/functions.html

macro_rules! function_item_matcher {
    (
        #[$meta: meta]
        $vis: vis async fn $name: ident ($arg: ident : $ty: ty) -> $ret:ty $block: block
    ) => {
        #[$meta]
        $vis async fn $name($arg: $ty) -> $ret $block
    };
}

如果 meta 的个数和 argument 的个数都不确定呢

#[allow(unused_variables)]
#[allow(dead_code)]
pub async fn foo(arg1: u8, arg2: u32, ) -> u8 { arg1 }

好,那就再改一改:

macro_rules! function_item_matcher {
    (
        $(#[$meta: meta])*
        $vis: vis async fn $name: ident ($($arg: ident : $ty: ty),* $(,)?) -> $ret:ty $block: block
    ) => {
        $(#[$meta])*
        $vis async fn $name($($arg: $ty),*) -> $ret $block
    };
}

还有个问题,这个 async 直接写下来的,要是没有 async 呢? 只需要加一个分支就好

macro_rules! function_item_matcher {
    (
        $(#[$meta: meta])*
        $vis: vis async fn $name: ident ($($arg: ident : $ty: ty),* $(,)?) -> $ret:ty $block: block
    ) => {
        $(#[$meta])*
        $vis async fn $name($($arg: $ty),*) -> $ret $block
    };

    (
        $(#[$meta: meta])*
        $vis: vis fn $name: ident ($($arg: ident : $ty: ty),* $(,)?) -> $ret:ty $block: block
    ) => {
        $(#[$meta])*
        $vis fn $name($($arg: $ty),*) -> $ret $block
    };
}

这样做不过是把我定义的函数照搬下来,有什么好处呢?好处就是你可以随意插入自己的代码

macro_rules! function_item_matcher {
    (
        $(#[$meta: meta])*
        $vis: vis fn $name: ident ($($arg: ident : $ty: ty),* $(,)?) -> $ret:ty $block: block
    ) => {
        println!("definition: {}({})", stringify!($name), stringify!($($arg: $ty),*));
        $(#[$meta])*
        $vis fn $name($($arg: $ty),*) -> $ret {
            print!("calling: {}(", stringify!($name));
            $(print!("{},", $arg);)*
            println!(")");

            $block
        }
    };
}

function_item_matcher!{
    #[allow(unused_variables)]
    #[allow(dead_code)]
    pub fn foo(arg1: u8, arg2: u32, ) -> u8 { arg1 }
}

foo(9, 8);

输出

definition: foo(arg1 : u8, arg2 : u32)
calling: foo(9,8,)

struct

struct 有两种

// struct struct
#[...]
struct A {
    ...
}

// tuple struct
#[...]
struct B(...);

所以对于第一种:

macro_rules! struct_item_matcher {
    (
        $(#[$meta: meta])*
        $vis: vis struct $name: ident {
            $(
                $(#[$field_meta: meta])*
                $field_vis: vis $field_name: ident : $field_ty: ty
            ),*

            $(,)?
        }
    ) => {
        $(#[$meta])*
        $vis struct $name {
            $(
                $(#[$field_meta])*
                $field_vis $field_name: $field_ty
            ),*
        }
    };

    // 针对 struct A; 的情况
    (
        $(#[$meta: meta])*
        $vis: vis struct $name: ident;
    ) => {
        $(#[$meta])*
        $vis struct $name;
    }
}

对于第二种 tuple struct 的情况处理起来大同小异,我就不写了 😂

posted on 2023-06-16 18:31  明天有风吹  阅读(210)  评论(0编辑  收藏  举报

导航

+V atob('d2h5X251bGw=')

请备注:from博客园