Getting a field from a struct based on fieldname in variable

Hi,

TL;DR: I'm trying to dynamically get a value in a struct by using the field name in a variable. I can't figure out how to do it, so I assume it's not idiomatic?

field = "a"
value = my_instance_of_mystruct.get(field);

I've found the std::slice::SliceIndex but can't figure out if I can apply it without re-implementing the trait for my struct.

Background
I have a struct, like this:

MyStruct {
  a: String, 
  b: String,
  c: f64
}

(In reality it has about 10-20 fields)

And I'm trying to convert these to Neon javascript objects. So for each of the 20 fields, I need to (depending on the field type):

        value = instance_of_mystruct.a;
        let js_value = cx.string(value);  // for strings
        let result = obj.set(cx, field, js_value);

Or:

        value = instance_of_mystruct.c;
        let js_value = cx.number(value);  // for numbers
        let result = obj.set(cx, field, js_value);

So in reality I want to loop this to not repeat myself, so I've made:

let strings = vec!["a", "b"];
let floats = vec!["c"];

for field in strings {
        value = instance_of_mystruct.get(field); // Won't work, get is not implemented for a struct
        let js_value = cx.number(value);  // for numbers
        let result = obj.set(cx, field, js_value);
}

Any good way to not having to repeat myself? Otherwise the code would become:

        value = instance_of_mystruct.a;
        let js_value = cx.string(value);  // for strings
        let result = obj.set(cx, "a", js_value);

        value = instance_of_mystruct.b;
        let js_value = cx.string(value);  // for strings
        let result = obj.set(cx, "b", js_value);

        value = instance_of_mystruct.c;
        let js_value = cx.number(value);  // for numbers
        let result = obj.set(cx, "c", js_value);

       [repeat 10-20 times more, since I have a lot of fields]

Aren't you looking for HashMap or some other dynamic mapping data structure?

1 Like

This is not possible in Rust in general, as Rust doesn't really have runtime reflection, which is usually necessary to implement such dynamic field getting. There are some crates for reflection (which require to add a special derive), but I wouldn't recommend.

Of course, you can do something like that manually, like:

fn get_string(&self, field: &str) -> &str {
    match field {
        "a" => &self.a,
        // …
    }
}

Usual answer to this question is to use a macro! (if it's impossible to use functions/loops, of course)

Something like that should work (untested)

macro_rules! set_string_fields {
    ( $mystruct:expr; $cx:expr; $obj:expr; $( $field:ident ),* ) => {
        $(
            let value = $mystruct.$field;
            let js_value = $cx.string(value);
            let result = $obj.set($cx, stringify!($field), js_value);
        )*
    }
}

fn main() {
    // …
    set_string_fields!(instance_of_mystruct; cx; obj; a, b, c, d, e);
}
2 Likes

Not really, it’s just a big struct. For the rust part that works super well, just complicated to convert to neon js objects :slight_smile: “the bridge” between rust and JavaScript.

It’s not a big issue, just means repeating code

Since the field names are fixed at compile time, you might be better served by an enum rather than dealing with strings:

pub enum MyStructField {
    A,
    B,
    C,
}

But to be able to have best of both worlds you could also implement From<&str> for MyStructField.

2 Likes

FromStr or TryFrom<&str> would probably be more appropriate, as they are allowed to produce an error when the given field name doesn't exist. The only option From<&str> has in this case is to abort the entire program via panic.

1 Like