7

In my program, I represent a history of changes to fields of some objects as follows:

struct FieldChange {
    property_name: &'static str,
    // some other fields
}

let history = Vec<FieldChange>::new();

I handle it like this:

match field_change.property_name {
    "name" => // do something,
    "age"  => // do something,
    _      => {}
}

To improve with readability and ease future refactoring, I would like to write something like this:

match field_change.property_name {
    nameof(Person::name) => // do something,
    nameof(Person::age)  => // do something,
    _                    => {}
}

where nameof generates a &str representation of the field, similar to nameof in C#.

The main point I'm looking for, is that the compiler can verify whether the fields exist (e.g., in this case, whether Person indeed has name and age fields). Is it possible to extract the field-names like this in Rust?

Community
  • 1
  • 1
xilec
  • 95
  • 1
  • 8

1 Answers1

6

No, but you can get something comparable with macros. Because macros solve everything! [1]

macro_rules! name_of {
    ($name:ident in $ty:ty) => {
        {
            #[allow(dead_code)]
            fn dummy(v: $ty) {
                let _ = &v.$name;
            }
            stringify!($name)
        }
    };

    ($name:ident) => {
        {
            let _ = &$name;
            stringify!($name)
        }
    };
}

struct Person {
    // 255 years should be enough for anybody.
    age: u8,
    name: String,
}

fn main() {
    let p = Person { age: 27, name: "John Smith".into() };
    println!("The {} of Person {} is: {}", name_of!(age in Person), name_of!(p), p.age);
}

If you try to use a name that doesn't exist, you get something that looks like this:

error: unresolved name `q`. Did you mean `p`? [--explain E0425]
  --> <anon>:28:78
28 |>     println!("The {} of Person {} is: {}", name_of!(age in Person), name_of!(q), p.age);
   |>                                                                              ^

Or like this:

error: attempted access of field `shoe_size` on type `Person`, but no field with that name was found
 --> <anon>:6:26
6 |>                 let _ = &v.$name;
  |>                          ^

[1]: Note: macros do not, in fact, solve everything.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
DK.
  • 55,277
  • 5
  • 189
  • 162
  • 1
    Thanks for answer. Unfortunately, this trick doesn't work with pattern matching. I assume, that procedural macros could help me when it comes – xilec Aug 18 '16 at 12:04
  • Ah, right; I forgot the `match` part. There's nothing I can do about that: if you want to assert the name *exists*, you have to do something that uses it, and you can't do that within a pattern. And no, procedural macros *will not* help, because they have the same limitations as regular macros here: *they cannot see names*. They would *have* to emit something similar. – DK. Aug 18 '16 at 12:06