如何通过字符串名称设置结构字段值?

Max*_*xie 4 struct rust

出于解释型编程语言的习惯,我想根据它们的键重写许多值。我假设我会将所有信息存储在为这个项目准备的结构中。所以我开始迭代:

struct Container {
    x: String,
    y: String,
    z: String
}
impl Container {
    // (...)    
    fn load_data(&self, data: &HashMap<String, String>) {
        let valid_keys = vec_of_strings![ // It's simple vector with Strings
            "x", "y", "z"
        ] ;
        for key_name in &valid_keys {
            if data.contains_key(key_name) {
                self[key_name] = Some(data.get(key_name);
                // It's invalid of course but
                // I do not know how to write it correctly.
                // For example, in PHP I would write it like this:
                // $this[$key_name] = $data[$key_name];
            }
        }
    }
    // (...)
}
Run Code Online (Sandbox Code Playgroud)

也许是宏?我尝试使用它们。key_name总是按原样解释,我无法获得价值key_name

如何在不重复每个值的代码的情况下执行此操作?

She*_*ter 6

对于宏,我总是主张从直接代码开始,然后看看有什么重复。在这种情况下,我们将从

fn load_data(&mut self, data: &HashMap<String, String>) {
    if let Some(v) = data.get("x") {
        self.x = v.clone();
    }
    if let Some(v) = data.get("y") {
        self.y = v.clone();
    }
    if let Some(v) = data.get("z") {
        self.z = v.clone();
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意差异的数量:

  1. 该结构必须采用 &mut self.
  2. 检查值是否存在然后单独获取它是低效的。
  3. 我们需要克隆该值,因为我们只有一个引用。
  4. 我们不能将 an 存储Option在 a 中String

一旦你的代码工作起来,你就可以看到如何抽象事物。总是从尝试使用“更轻”的抽象(函数、特征等)开始。只有在筋疲力尽之后,我才会开始引入宏。让我们开始使用stringify

if let Some(v) = data.get(stringify!(x)) {
    self.x = v.clone();
}
Run Code Online (Sandbox Code Playgroud)

然后你可以提取一个宏:

macro_rules! thing {
    ($this: ident, $data: ident, $($name: ident),+) => {
        $(
            if let Some(v) = $data.get(stringify!($name)) {
                $this.$name = v.clone();
            } 
        )+
    };
}

impl Container {
    fn load_data(&mut self, data: &HashMap<String, String>) {
        thing!(self, data, x, y, z);
    }
}

fn main() {
    let mut c = Container::default();
    let d: HashMap<_, _> = vec![("x".into(), "alpha".into())].into_iter().collect(); 

    c.load_data(&d);

    println!("{:?}", c);
}
Run Code Online (Sandbox Code Playgroud)

完全披露:我认为这不是一个好主意。