#struct-fields #getter-setter #proc-macro #setter #getter #setattr #getattr

field_accessor

通过字符串动态获取和设置结构体字段的过程宏

10个版本 (4个破坏性版本)

0.5.2 2022年8月29日
0.5.1 2022年5月10日
0.4.3 2022年5月8日
0.3.0 2022年5月5日
0.1.0 2022年5月4日

#20#setter

Download history 128/week @ 2024-03-11 68/week @ 2024-03-18 101/week @ 2024-03-25 107/week @ 2024-04-01 98/week @ 2024-04-08 86/week @ 2024-04-15 53/week @ 2024-04-22 63/week @ 2024-04-29 46/week @ 2024-05-06 47/week @ 2024-05-13 25/week @ 2024-05-20 10/week @ 2024-05-27 15/week @ 2024-06-03 18/week @ 2024-06-10 19/week @ 2024-06-17 16/week @ 2024-06-24

每月69次下载
2 crate 使用

MIT 许可证

280KB
185

field_accessor

通过这个过程宏,你可以通过一个 String 类型的变量动态地获取和更新结构体的字段。如果你在编译时不知道想要哪个字段,它对你来说可能很有用。这个功能与Python的 getattrsetattr 类似。

安装

[dependencies]
field_accessor = "0"

关于此宏

此宏为结构体提供了四种方法。仅对 getset,为了处理每个字段的类型,我定义了 GetterSetter<T> 特性并为每种类型实现了它。

trait GetterSetter<T> {
    fn get(&self, field_string: &String) -> Result<&T, String>;
    fn set(&mut self, field_string: &String, value: T) -> Result<(), String>;
}

//implement for each type
impl GetterSetter<String> for StructName {
    fn get(&self, field_string: &String) -> Result<&String, String>;
    fn set(&mut self, field_string: &String, value: String) -> Result<(), String>;
}
impl GetterSetter<u32> for StructName {
    fn get(&self, field_string: &String) -> Result<&u32, String>;
    fn set(&mut self, field_string: &String, value: u32) -> Result<(), String>;
}
etc...

get

fn get(&self, field_string: &String) -> Result<&T, String>;

它返回字段的值。请注意,你需要指定返回类型。

get_mut

fn get_mut(&mut self, field_string: &String) -> Result<&mut T, String>;

返回对应于 field_string 的字段的可变引用。

set

fn set(&mut self, field_string: &String, value: String) -> Result<(), String>;

它更新字段的值。

take

fn take(&mut self, field_string: &String) -> Result<T, String>;

用 T 的默认值替换字段的值,返回之前的字段值。

swap

fn swap(&mut self, field_string: &String, field_string_y: &String) -> Result<(), String>;

交换两个字段的值,不初始化任何一个。

replace

fn replace(&mut self, field_string: &String, src: T) -> Result<T, String>;

将 src 移入字段,返回之前的字段值。

getenum

fn getenum(&self, field_string: &String) -> Result<(StructName)FieldEnum, String>;

它返回字段的值,就像 get 方法一样,但返回类型是枚举。当字段类型不同时,此方法很有用。我将在后面解释枚举。

getstructinfo

fn getstructinfo(&self) -> (StructName)StructInfo;

你可以提取结构体的字段名称、类型和结构体名称。

使用方法和示例

run

use field_accessor::FieldAccessor;

#[derive(FieldAccessor)]
struct Dog {
    name: String,
    age: u32,
    life_expectancy: u32,
}

fn main() {
    let mut dog = Dog {
        name: "Taro".to_string(),
        age: 3,
        life_expectancy: 9,
    };

    let field_name = "name".to_string();
    let value_to_update = "Jiro".to_string();
    dog.set(&field_name, value_to_update).unwrap();
    let value_on_error;
    let fieldvalue: &String = match dog.get(&"invalid_field".to_string()) {
        Ok(value) => value,
        Err(_) => {value_on_error = "Ken".to_string(); &value_on_error},
    };
    println!("{:?}", fieldvalue);

    let field_name = "age".to_string();
    let value_to_update = 4u32;
    dog.set(&field_name, value_to_update).unwrap();
    let fieldvalue: &u32 = dog.get(&field_name).unwrap();
    println!("{:?}", fieldvalue);

    let field_name = "life_expectancy".to_string();
    let value_to_update = 10u32;
    dog.set(&field_name, value_to_update).unwrap();
    let fieldvalue: &u32 = dog.get(&field_name).unwrap();
    println!("{:?}", fieldvalue);

}

输出

"Ken"
4
10

此代码在编译时生成。

已知问题

你需要指定返回值的类型。如果没有给出,编译器无法推断类型。这种限制减少了使用此宏的便利性。

#[derive(FieldAccessor)]
struct Dog {
    name: String,
    age: u32,
    life_expectancy: u32,
}

let mut dog = Dog {
    name: "Taro".to_string(),
    age: 3,
    life_expectancy: 9,
};
let fields = vec![
    "name".to_string(),
    "age".to_string(),
    "life_expectancy".to_string(),
]
for field_name in fields.into_iter(){
    let fieldvalue = dog.get(&field_name).unwrap();
};

此代码会产生错误。

let fieldvalue = dog.get(&field_name).unwrap();
    ----------       ^^^ cannot infer type for type parameter `T` declared on the trait `GetterSetter`
    |
    consider giving `fieldvalue` the explicit type `&T`, where the type parameter `T` is specified

一种解决方法是使用 getenum 替换 get。此宏在幕后为你定义了 (struct name)FieldEnum,如下所示。

enum DogFieldEnum {
    name(String),
    age(u32),
    life_expectancy(u32),
}

您可以将此用作返回类型。使用此枚举,您可以在不关心字段类型的情况下获取任何字段的值。

let mut dog = Dog {
    name: "Taro".to_string(),
    age: 3,
    life_expectancy: 9,
};
let fields = vec![
    "name".to_string(),
    "age".to_string(),
    "life_expectancy".to_string(),
];
let mut fieldvalues: Vec<DogFieldEnum> = vec![];
for field_name in fields.into_iter(){
    fieldvalues.push(dog.getenum(&field_name).unwrap());
};
assert_eq!(fieldvalues[0], DogFieldEnum::name("Taro".to_string()));
assert_eq!(fieldvalues[1], DogFieldEnum::age(3));

获取结构体信息

您可以通过调用 getstructinfo 来获取结构体的信息,使用 (字段名)StructInfo

(字段名)StructInfo 的定义

struct DogStructInfo {
    field_names: Vec<String>,
    field_types: Vec<String>,
    struct_name: String
}

示例

let info = dog.getstructinfo();
println!("{:?}", info);
for i in info.field_names.iter() {
    let fieldvalue: DogFieldEnum = dog.getenum(i).unwrap();
    println!("{:?}", fieldvalue);
}

输出

DogStructInfo { field_names: ["name", "age", "life_expectancy"], field_types: ["String", "u32", "u32"], struct_name: "Dog" }

name("Jiro")
age(4)
life_expectancy(10)

作者

Tomohiro Endo ([email protected])

依赖

约1.5MB
约35K SLoC