Yuri Gor
Yuri Gor

Reputation: 1463

Storing mutable references owned by someone else

I want to store structs in HashMap but also reference to same structs inside another struct vector field.

Let's say I have a tree build of two types of structs Item and Relation.

I am storing all the relations in HashMap by their id, but I also want to fill each item.out_relations vector with mutable references to same Relation structs which are owned by HashMap.

Here is my Item:

pub struct Item<'a> {
  pub id: oid::ObjectId,
  pub out_relations: Vec<&'a mut Relation>, // <-- want to fill this
}

And when I am iterating over my relations got from DB I am trying to do smth like this:

let from_id = relation.from_id; //to find related item
item_map // my items loaded here already
.entry(from_id)
.and_modify(|item| { 
  (*item)
    .out_relations.push(
      relation_map.try_insert(relation.id, relation).unwrap() // mut ref to relation expected here
    )
  }
);

For now compiler warns try_insert is unstable feature and points me to this bug But let's imagine I have this mutable ref to relation owned already by HashMap- is it going to work?

Or this will be again some ref lives not long enough error? What are my options then? Or I better will store just relation id in item out_relations vector rather then refs? And when needed I will take my relation from the hashmap?

Upvotes: 0

Views: 296

Answers (2)

Jeffrey Rennie
Jeffrey Rennie

Reputation: 388

I agree with Oussama Gammoudi's diagnosis, but I'd like to offer alternative solutions.

The issue with reference counting is that it kills performance.

In your vector, can you store the key to the hash map instead? Then, when you need to get the value from the vector, get the key from the vector and fetch the value from the hash map?

Another strategy is to keep a vector of the values, and then store indices into the vector in your hash map and other vector. This Rust Keynote speaker describes the strategy well: https://youtu.be/aKLntZcp27M?t=1787

Upvotes: 1

Oussama Gammoudi
Oussama Gammoudi

Reputation: 761

This is called shared mutability, and it is forbidden by the borrow checker. Fortunately Rust offers safe tools to achieve this. In your case you need to use Rc<RefCell>, so your code would be:

pub struct Item {
  pub id: oid::ObjectId,
  pub out_relations: Vec<Rc<RefCell<Relation>>>,
}

And when I am iterating over my relations got from DB I am trying to do smth like this:

let relation = Rc::new(RefCell::new(relation));

let from_id = relation.borrow().from_id; // assuming id is a Copy type
item_map // my items loaded here already
.entry(from_id)
.and_modify(|item| { 
  (*item)
    .out_relations.push(
      relation_map.try_insert(relation.id, relation.clone()).unwrap() // mut ref to relation expected here
    )
  }
);

If you want to mutate relation later, you can use .borrow_mut()

relation.borrow_mut().from_id = new_id;

Upvotes: 1

Related Questions