-4

I have a vector of structures. I want to add one additional field to each element. What's the best way to do that?

Something like this:

// Pseudo code

let items = vec![elem1, elem2, elem3, elem4];
for x in items {
    // Something like this
    x["some_additional_key"] = get_data(x);
} 

//
// Now I have items[i].some_additional_key in each element
Lukas Kalbertodt
  • 79,749
  • 26
  • 255
  • 305
Bingi
  • 59
  • 6
  • 7
    You already [asked the exact same question on /r/rust](https://www.reddit.com/r/rust/comments/76grm4/add_a_key_to_each_element_of_a_vector/) and got an answer there. As the people on Reddit already explained: what you want is not possible in Rust. Structs have a fixed layout at compile time and cannot be modified at runtime. To have an arbitrary key-value store, use a `HashMap` or something similar. – Lukas Kalbertodt Oct 15 '17 at 08:23
  • 1
    But instead of using a `HashMap` you should probably do one of these things: switch to a language like JavaScript that can actually do what you want *or* try to think about your program in another way. Literally translating your way of thinking in one programming language to another programming language doesn't work, especially not with Rust. If you rethink your problem, you'll likely come up with a far better solution. – Lukas Kalbertodt Oct 15 '17 at 08:26

1 Answers1

4

Rust is a statically-typed language; you may be familiar with other similar languages like C++, Java or Swift. In these languages, the members, types, and layout of a struct are fixed when the program is compiled.

Because of this, there's no way to add a new struct field at runtime — no "ifs", "ands", or "buts" — you can't do it.

Instead, you have to model that dynamic nature some other way:

  1. Use a type that allows for arbitrary expansion. HashMap and BTreeMap (and many other similar types) allow you to have an arbitrary number of key-value pairs. Under the hood, this is basically how many dynamic languages work - a mapping of strings to arbitrary values:

    use std::collections::HashMap;
    
    #[derive(Debug, Default)]
    struct Element(HashMap<String, u8>);
    
    fn get_data(_: &Element) -> u8 {
        42
    }
    
    fn main() {
        let mut items = vec![
            Element::default(),
            Element::default(),
            Element::default(),
            Element::default(),
        ];
    
        for x in &mut items {
            let value = get_data(x);
            x.0
                .entry("some_additional_key".to_string())
                .or_insert(value);
        }
    }
    
  2. Use a type that allows for specific expansion. Option allows for a value to be present or not:

    #[derive(Debug, Default)]
    struct Element {
        some_additional_key: Option<u8>,
    }
    
    fn get_data(_: &Element) -> u8 {
        42
    }
    
    fn main() {
        let mut items = vec![
            Element::default(),
            Element::default(),
            Element::default(),
            Element::default(),
        ];
    
        for x in &mut items {
            let value = get_data(x);
            x.some_additional_key = Some(value);
        }
    }
    
  3. Use composition. Create a new type that wraps your existing type:

    #[derive(Debug, Default)]
    struct Element;
    
    #[derive(Debug)]
    struct EnhancedElement {
        element: Element,
        some_additional_key: u8,
    }
    
    fn get_data(_: &Element) -> u8 {
        42
    }
    
    fn main() {
        let items = vec![
            Element::default(),
            Element::default(),
            Element::default(),
            Element::default(),
        ];
    
        let enhanced: Vec<_> = items
            .into_iter()
            .map(|element| {
                let some_additional_key = get_data(&element);
                EnhancedElement {
                    element,
                    some_additional_key,
                }
            })
            .collect();
    }
    

See also:

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366