首页 > 解决方案 > 如何在 Rust 中以编程方式读取属性

问题描述

我想以编程方式读取属性。例如,我有一个结构,它具有附加到每个字段的属性:

#[derive(Clone, Debug, PartialEq, Message)]
pub struct Person {
    #[prost(string, tag="1")]
    pub name: String,
    /// Unique ID number for this person.
    #[prost(int32, tag="2")]
    pub id: i32,
    #[prost(string, tag="3")]
    pub email: String,
    #[prost(message, repeated, tag="4")]
    pub phones: Vec<person::PhoneNumber>,
}

来源

我想找到与电子邮件字段关联的标签。

我希望有一些这样的代码可以在运行时获取标签:

let tag = Person::email::prost::tag;

标签: rust

解决方案


由于属性在编译时只读,因此您需要编写一个过程宏来解决此类问题。

您可以在宏中找到具有以下指示符的信息。

  • 字段名称与ident
  • 属性内容与meta

找到字段名称和元数据后,您可以将字符串化结果与宏中给定的输入参数匹配,如下所示:

macro_rules! my_macro {
    (struct $name:ident {
        $(#[$field_attribute:meta] $field_name:ident: $field_type:ty,)*
    }) => {
        struct $name {
            $(#[$field_attribute] $field_name: $field_type,)*
        }

        impl $name {
            fn get_field_attribute(field_name_prm : &str) -> &'static str {
                let fields = vec![$(stringify!($field_name,$field_attribute)),*];
                let mut ret_val = "Field Not Found";

                fields.iter().for_each(|field_str| {
                    let parts : Vec<&str> = field_str.split(' ').collect();
                    if parts[0] == field_name_prm{
                        ret_val = parts[2];
                    }
                });
                ret_val
            }
        }
    }
}

my_macro! {
    struct S {
        #[serde(default)]
        field1: String,
        #[serde(default)]
        field2: String,
    }
}

请注意,它假定结构中的每个字段都有一个属性。每个字段声明都以,包含最后一个字段结束。但是通过对正则表达式进行一些修改,您也可以使其可用于可选属性。

在Playground中工作的解决方案

有关指示符的更多信息,请参见参考

您也可以在此处快速查看程序宏


推荐阅读