Access struct attributes by string


#1

Is there a way I can access an attribute of a struct by a string?


#2

No. Names of fields of a struct don’t even exist in the running program. They’re completely removed at compile time.

You can map names to fields manually (and write a macro if you need this often):

match name {
   "foo" => s.foo,
   "bar" => s.bar,
   "baz" => s.baz,
   _ => panic!("unknown field"),
}

However, depending on what you’re really trying to achieve, there may be better ways than accessing fields by string.

For example, if you’re reading some kind of input into a struct, see serde. If you’re mapping options to values, perhaps a regular Rust enum would work.


#3

Here is a method that does what you asked, but as @kornel wrote there is going to be a better way to do what you are trying to do. If you say more about your use case then people can provide better guidance.


#[macro_use]
extern crate serde_derive;

extern crate serde;
extern crate serde_value;

use serde::Serialize;
use serde::de::DeserializeOwned;
use serde_value::Value;

#[derive(Serialize)]
struct Justmike2000 {
    a: String,
    b: String,
    c: u64,
}

fn get_field_by_name<T, R>(data: T, field: &str) -> R
where
    T: Serialize,
    R: DeserializeOwned,
{
    let mut map = match serde_value::to_value(data) {
        Ok(Value::Map(map)) => map,
        _ => panic!("expected a struct"),
    };

    let key = Value::String(field.to_owned());
    let value = match map.remove(&key) {
        Some(value) => value,
        None => panic!("no such field"),
    };

    match R::deserialize(value) {
        Ok(r) => r,
        Err(_) => panic!("wrong type?"),
    }
}

fn main() {
    let j = Justmike2000 {
        a: "string A".to_owned(),
        b: "string B".to_owned(),
        c: 1,
    };

    let a: String = get_field_by_name(&j, "a");
    println!("a = {:?}", a);

    let b: String = get_field_by_name(&j, "b");
    println!("b = {:?}", b);

    let c: u64 = get_field_by_name(&j, "c");
    println!("c = {:?}", c);
}