3

I am using a HashMap to store an enum. I'd like to get a value from the HashMap and if the value is a specific enum variant, I'd like to insert a modified copy of the value back in the HashMap.

The code I came up with looks like this:

if let Node::LeafNode(mut leaf_node) = *(self.pages.get(&page).unwrap()) {
   let mut leaf_node = leaf_node.clone();
   // ...
   self.pages.insert(leaf_page,Node::LeafNode(leaf_node)); 
}

This does not compile because the borrow of self.pages lasts until the end of the if let-block and self.pages.insert is a mutable borrow.

I have tried to shadow the value of the HashMap with a copy of the value, but this does not end the borrow. Usually I would use a {} block to limit the borrow, but this seems to be not possible in match or if let.

What is the idiomatic way to end a borrow so that I can get a new mutable borrow?

cosinus
  • 33
  • 5

2 Answers2

4

This is not possible at the moment. What you want is called non-lexical borrows and it is yet to be implemented in Rust. Meanwhile, you should use Entry API to work with maps - in most cases it should be sufficient. In this particular case I'm not sure if entries are applicable, but you can always do something like

let mut result = None;
if let Some(&Node::LeafNode(ref leaf_node)) = self.pages.get(&page) {
    let mut leaf_node = leaf_node.clone();
    // ...
    result = Some((leaf_page, leaf_node));
}

if let Some((leaf_page, leaf_node)) = result {
    self.pages.insert(leaf_page, leaf_node);
}

It is difficult to make the code above entirely correct given that you didn't provide definitions of Node and self.pages, but it should be approximately right. Naturally, it would work only if leaf_page and leaf_node do not contain references to self.pages or self, otherwise you won't be able to access self.pages.

Vladimir Matveev
  • 120,085
  • 34
  • 287
  • 296
  • Do we actually need `Entry` here? I would have thought that `HashMap::get_mut` should be sufficient. – fjh Nov 03 '15 at 10:07
  • I'm not sure about entries in this particular case, but they do provide a way to work around some of lexical borrow limitations, that's why I mentioned them. – Vladimir Matveev Nov 03 '15 at 10:09
  • And I don't think that `get_mut` is sufficient here because the map itself is modified, not the value. – Vladimir Matveev Nov 03 '15 at 10:11
  • Yeah, you're right. I thought that the code was just replacing an entry in the map, but it's manipulating an entry at a different key. Sorry. – fjh Nov 03 '15 at 10:15
  • Lovely... works. a little unexpected for a rust novice that an Option is needed. Would be a great addition to Rust to have more control ending borrow where needed. I have used a match to simplify it a little, but it's exactly your proposal. thanks! – cosinus Nov 03 '15 at 14:02
  • Yes, non-lexical borrows will make life much easier. Actually, right now a work is being done to allow them and some other things (namely, implementation of MIR, mid-level intermediate language, and its usage in the compiler), so one can hope that this will be fixed in relatively close time frame. – Vladimir Matveev Nov 03 '15 at 14:22
0

Here is Vladimir's solution using match:

let mut result = match self.pages.get(&page) {
    Some(&Node::LeafNode(ref leaf_node)) => Some(leaf_node.clone()),
    _ => None,
};
if let Some(leaf_node) = result {
    // ...
    self.pages.insert(page_number, Node::LeafNode(leaf_node));
};
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
cosinus
  • 33
  • 5