HashMap of a Vector of Objects

Hello,

Rust newbie here (I’m about 2-3 weeks in). I was looking at Vectors and HashMaps, and made me wonder if I can create a HashMap of references to a vector’s elements. My thought process was that it would be nice to be able to look up an object by one of it’s members while still maintaining the the collection by modifying the Vector.

I tried this in a multitude of ways, but continued to get lifetime and ownership errors until I landed at this implementation:

use std::collections::HashMap;

#[derive(Clone, Debug)]
struct SomeMessage {
    name: String,
    id: u32,
    info: u64,
}
#[derive(Clone, Debug)]
struct QuickLookup<'a> {
    vector: Vec<SomeMessage>,
    hashmap: HashMap<u32, &'a SomeMessage>,
}
impl<'a> QuickLookup<'a> {
    fn new(messages: Vec<SomeMessage>, hashmap: HashMap<u32, &'a SomeMessage>) -> QuickLookup {
        QuickLookup {
            vector: messages,
            hashmap,
        }
    }
    fn populate(&'a mut self) {
        for message in self.vector.iter() {
            self.hashmap.insert(message.id, &message);
        }
    }
}
fn main() -> () {
    let message_1 = SomeMessage {
        name: "Msg1".to_string(),
        id: 5,
        info: 88,
    };
    let message_2 = SomeMessage {
        name: "Msg2".to_string(),
        id: 7,
        info: 33,
    };
    let mut message_vec = Vec::new();
    message_vec.push(message_1);
    message_vec.push(message_2);

    let hashmap = HashMap::new();

    let mut quick = QuickLookup::new(message_vec, hashmap);

    quick.populate(); //Can't borrow anything from quick after this line

    println!("{:?}", quick.vector); //Doesn't work
    println!("{:?}", quick.hashmap); //Doesn't work
    quick.populate();  //Also doesn't work 
}

I had trouble bringing the HashMap into the struct directly in new(), so I made a populate() method dedicated to updating hashmap.

This code “feels” like the closest I’ve been able to get to making this idea work, as I am able to get the struct and it’s methods to compile. However, the unexpected (to me) side effect is that I cannot seem to use the struct at all after calling populate on it.

error[E0502]: cannot borrow `quick.vector` as immutable because it is also borrowed as mutable
  --> examples/HashMap_of_Vec.rs:52:22
   |
50 |     quick.populate(); //Can't borrow anything from quick after this line
   |     ----- mutable borrow occurs here
51 | 
52 |     println!("{:?}", quick.vector);
   |                      ^^^^^^^^^^^^
   |                      |
   |                      immutable borrow occurs here
   |                      mutable borrow later used here

error[E0502]: cannot borrow `quick.hashmap` as immutable because it is also borrowed as mutable
  --> examples/HashMap_of_Vec.rs:53:22
   |
50 |     quick.populate(); //Can't borrow anything from quick after this line
   |     ----- mutable borrow occurs here
...
53 |     println!("{:?}", quick.hashmap); //locked out after this line.
   |                      ^^^^^^^^^^^^^
   |                      |
   |                      immutable borrow occurs here
   |                      mutable borrow later used here

error: aborting due to 2 previous errors

I seem to be lacking some knowledge when it comes to the understanding of structs, ownership, lifetimes or a combination of all three. I’ve re-read over the rust book sections that I believe apply multiple times, but something is just not clicking. I’d appreciate if anyone had some input to steer me in the right direction of understanding this problem.

The problem with this approach is that you’re creating a self-referential struct, which is impossible to make in rust using only references, like you’ve done. The technical problem is that moving the structure invalidates the reference, because the reference will point to something that is no longer there (It will have been moved).

There are a few ways to fix this, with the “easiest” way being an Rc<T>, which will live forever as long as it has a single copy of itself. I’d probably go for using a HashMap<usize> because that would instead be indices, but this could potentially lead to problems when trying to remove items from the Vec.

Thank you for the input. I’m glad to at least know what I was trying to do is impossible.

I had seen Rc brought up while I searched for similar problems to mine. I’ll have to take a look at it eventually. Though, it might be something I put off for later while I still figure out the basics.

Thanks again!

1 Like