Lifetimes question, HashMap with references to Vec

A program I'm writing reads in some data and creates a Vec of data objects, each containing a name (String,RegistryData).
I then want to index it with a HashMap, so I can quickly access a named item. I want to keep the Vec also, rather than just placing each item into a HashMap from the beginning.

I've tried the following where my HashMap<String,&RegistryData> would store a reference to an item in the Vec, but I think this fails because I'm not specifying lifetimes. I've tried adding the suggested changes from the compiler, but still can't get to a working version.

Can somebody point me in the right direction?
Is it possible to modify this example so that I can store references to the Vec's contents in the HashMap?

use std::collections::HashMap;

// In real program, this would be a struct
type RegistryData = i32;

#[derive(Debug)]
struct Registry {
    data: Vec<(String,RegistryData)>,   // names and data in Vec format
    map: HashMap<String,&RegistryData>, // map of name -> data
}

impl Registry {
    pub fn new() -> Self {
        // create some data
        let data = [(String::from("foo"), 7), (String::from("bar"), 42)].to_vec();
        // create empty hashmap
        let mut map:HashMap<String,&RegistryData> = HashMap::new();

        // walk through data, adding reference to each to hashmap
        for item in &data {
            map.insert(item.0.clone(), &item.1);
        }

        Self {
            data: data,
            map: map
        }
    }
}

fn main() {
    let registry = Registry::new();

    println!("{:#?}", registry);
}

You are creating a self-referential type, which is difficult to do in Rust. Even if you add a lifetime, you will get other errors about moving out of borrowed values, and returning a reference of a local variable.

One easy way to do this is with reference counting: Rust Playground (I changed your RegistryData type to an actual struct, because i32 is Copy and doesn't make much sense to ref-count or borrow in this case).

(edit: If you want to make the interior data of Rc<T> mutable, you can combine it with RefCell. For multi-threaded interior mutability, use Arc<Mutex<T>>. This suggestion is only included for completeness, since I want to show that both data and map fields indeed point to the same location: Rust Playground)

If you want to get your hands dirty, you could try making a pinning type, but it is more involved and you will probably end up needing to learn some rather esoteric concepts to use it.

1 Like

You want to avoid putting the data and references to it in the same struct.
The data needs to exist first. Then you can make whatever you like to point to it.
But you cannot put the data and the references into the same struct.

3 Likes

This is a great point! To add to it, the data must also be placed first. When it is moved (e.g. by returning it from a function) that can be thought of as a memcpy literally moving the data to another location in memory. Any borrows or pointers would be immediately invalidated by the move. (This is what Pin wants to avoid.)

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.