18

Is there some equivalent of JS's Object.keys() for Rust's struct?

I need something to generate CSV headers (I use rust-csv) from structure field names.

struct Export {
    first_name: String,
    last_name: String,
    gender: String,
    date_of_birth: String,
    address: String
}

//... some code

let mut wrtr = Writer::from_file("/home/me/export.csv").unwrap().delimiter(b'\t');

wrtr.encode(/* WHAT TO WRITE HERE TO GET STRUCT NAMES as tuple of strings or somethings */).is_ok()
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Alexander Arutinyants
  • 1,619
  • 2
  • 23
  • 49
  • You can’t do that without a *rustc* plugin (works only on a nightly). – mcarton May 10 '16 at 14:39
  • Thanks @mcarton. I'll read about [Compiler Plugins](https://doc.rust-lang.org/book/compiler-plugins.html). I really like to keep certain data in one place, so using field's name could be great. And I could move to nightly, so any help appreciated. – Alexander Arutinyants May 10 '16 at 14:57
  • 3
    Unlikely to need a compiler plugin for your actual usecase; can probably use `#[derive(RustcDecodable)]` as [described in the repository](https://github.com/BurntSushi/rust-csv). Doesn't answer the question you are asking though, which is a generic way to list **any** structs field names. My old friend, the [XY Problem](http://xyproblem.info/). – Shepmaster May 10 '16 at 15:04
  • Now that I think of it, syntax plugins are expanded before type analysis, so it would be rather tricky even in a plugin. There is some starter doc in [the book](https://doc.rust-lang.org/book/compiler-plugins.html). You’d need a [macro](https://manishearth.github.io/rust-internals-docs/rustc_plugin/registry/struct.Registry.html) and maybe a custom attribute for the type to inspect. – mcarton May 10 '16 at 15:06
  • @Shepmaster `#[derive(RustcDecodable)]` is a plugin :), but yeah, let’s not reinvent the wheel. Also serde is usually better. – mcarton May 10 '16 at 15:08
  • @mcarton an interesting philosophical question: "Is a plugin that is always plugged-in still a plugin?" ^_^. But as you pointed out, the important thing is to reuse existing work and potentially avoid needing to use the nightly compiler. Serde even has stable support (via `build.rs`) for pre-processing "syntax extensions". – Shepmaster May 10 '16 at 15:34
  • 1
    This can be also done by implementing your own procedural macro where you parse AST and create a trait implementation for your struct which returns the field names. I have done it personally 2 weeks ago but the code is not public and this question has already been closed. So just letting you know this is possible by some `#[derive(Introspection)]`. – VP. Feb 27 '17 at 11:07
  • 1
    @AlexanderArutinyants I will make it public during a week. I may tell you here when it happens if you are interested in this solution (all it needs is just to add `#[derive(Introspection)]` to your enum or struct. – VP. Mar 02 '17 at 07:36
  • @AlexanderArutinyants [here it is](https://github.com/vityafx/introspection). Hope this helps. I have not provide enough docs yet but you may already use it. I have plans for extending it's functionality in some future but somethings are just impossible to do :( – VP. Mar 02 '17 at 10:44
  • use serde_aux::prelude::*; See the usage example [serde-aux](https://docs.rs/serde-aux/latest/serde_aux/serde_introspection/fn.serde_introspect.html) – Claudio Fsr Apr 05 '23 at 17:46

1 Answers1

30

The current main method of metaprogramming in Rust is via macros. In this case, you can capture all the field names and then add a method that returns string forms of them:

macro_rules! zoom_and_enhance {
    (struct $name:ident { $($fname:ident : $ftype:ty),* }) => {
        struct $name {
            $($fname : $ftype),*
        }

        impl $name {
            fn field_names() -> &'static [&'static str] {
                static NAMES: &'static [&'static str] = &[$(stringify!($fname)),*];
                NAMES
            }
        }
    }
}

zoom_and_enhance!{
struct Export {
    first_name: String,
    last_name: String,
    gender: String,
    date_of_birth: String,
    address: String
}
}

fn main() {
    println!("{:?}", Export::field_names());
}

For advanced macros, be sure to check out The Little Book of Rust Macros.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • 2
    Similar question and solution: http://stackoverflow.com/a/29986760/996886 – melak47 May 10 '16 at 16:26
  • 1
    @melak47 good point! Do you think that this question should be marked as a duplicate? – Shepmaster May 10 '16 at 17:13
  • can you enhance it..? hold on, I'll enhance it. – jayphelps Dec 25 '16 at 18:12
  • It seems like I can't add an answer here, so I did over at the other linked question. My added complication was that I also needed `#[derive()]` macros to work on the struct, so the solution here was incompatible with that. My write-down of a workaround is here: https://stackoverflow.com/a/73375434/2352071 – dlaehnemann Aug 16 '22 at 14:21