0

Assume we have a Person struct and a PersonName struct that used by a field, like the following:

struct Person {
    name: PersonName,
    age: u8,
}

struct PersonName {
    name: String,
    middle_name: Option<String>,
    surname: Option<String>,
}

Create the struct:

let foo = Person { name: PersonName{name: String::from("bar"), middle_name: None, surname: String::from("baz") }, age: 16u8 };

How can I use this string to gain access to the struct field specified in it?

let foo_surname = eval("name.surname") // returns "baz"
// struct evaluator returns the value of specific field
// i.e.: field_path: "my_struct_field.sub_field1.sub_sub_field_1"
fn eval(field_path: String) -> ...{}

Is there any way to do this without writing a custom evaluator?

Dentrax
  • 574
  • 8
  • 22
  • You can do this by creating you own trait and create you own derive macro for this trait. But it is VERY complicated to be described here. – Dmitry Apr 02 '21 at 08:52

2 Answers2

1

There is no way to do this. Rust is a statically compiled language where this kind of information doesn't existing anymore during the program runtime.

mcarton
  • 27,633
  • 5
  • 85
  • 95
0

There are at least two possible approaches.

If you must return the actual heterogeneous (u8 vs String) data from that eval(), you'll need to wrap it in an enum and write a match on the field_path along the lines of this answer: https://stackoverflow.com/a/73676114/22362561

However, in many cases you can get around this by serializing the data to hold the heterogeneous types. Here is a solution using miniconf (disclaimer: I develop that crate) with '/' as the path hierarchy separator and JSON as the serialization format:

use miniconf::{JsonCoreSlash, Tree};

#[derive(Tree)]
struct Person {
    #[tree]
    name: PersonName,
    age: u8,
}

#[derive(Tree)]
struct PersonName {
    name: String,
    middle_name: Option<String>,
    surname: Option<String>,
}

impl Person {
    fn eval(&self, key: &str) -> String {
        let mut buf = vec![0; 128];
        let len = self.get_json(key, &mut buf).unwrap();
        buf.truncate(len);
        String::from_utf8(buf).unwrap()
    }
}

fn main() {
    let foo = Person {
        name: PersonName {
            name: "bar".into(),
            middle_name: None,
            surname: Some("baz".into()),
        },
        age: 16u8,
    };
    assert_eq!(foo.eval("/name/surname"), "\"baz\"");
    assert_eq!(foo.eval("/age"), "16");
}

Note that you can plug in other serialization backends (ser_raw comes to mind) and also other path hierarchy separators.

Similar: