2

Edit Note: This code now compile see What are non-lexical lifetimes?.


I have two HashMaps and want to swap a value between them under certain conditions. If the key does not exist in the second HashMap, it should be inserted. I do not want to clone the value, since that is too expensive.

The (simplified) critical code that is not working is as follows:

use std::collections::HashMap;
use std::collections::hash_map::Entry;
use std::mem;

#[derive(Debug)]
struct ExpensiveStruct {
    replace: bool,
    // imagine a lot of heap data here
}

fn main() {
    let mut hm : HashMap<usize, ExpensiveStruct> = HashMap::new();
    let mut hm1 : HashMap<usize, ExpensiveStruct> = HashMap::new();

    let dummy = ExpensiveStruct { replace: false };

    hm.insert(1, ExpensiveStruct { replace: true});
    hm1.insert(1, ExpensiveStruct { replace: true});

    match hm1.get_mut(&1) {
        Some(ref mut x) =>
            match hm.entry(1) {
                Entry::Occupied(mut y) => { if y.get().replace { 
                    mem::swap(x, &mut y.get_mut()); 
                    } 
                },
                Entry::Vacant(y) => { y.insert(mem::replace(x, dummy)); }
            },
        None => {}
    }

    println!("{:?}", hm);
}

(On the Rust Playground)

I get the error:

error[E0597]: `y` does not live long enough
  --> src/main.rs:28:9
   |
23 |                 mem::swap(x, &mut y.get_mut());
   |                                   - borrow occurs here
...
28 |         },
   |         ^ `y` dropped here while still borrowed
29 |         None => {}
30 |     }
   |     - borrowed value needs to live until here

I am really confused about this borrow problem and I do not see a way to fix it. If I replace the Entry by a match hm.get_mut(1), I cannot insert in the None case, since the matching mutably borrows the HashMap.

Stargateur
  • 24,473
  • 8
  • 65
  • 91
Ben Ruijl
  • 4,973
  • 3
  • 31
  • 44

1 Answers1

5

You're giving references to references where you should be giving references.

&mut y.get_mut()

for instance is

&mut &mut ExpensiveStruct

and you're having a simular issue with

match hm1.get_mut(&1) {
    Some(ref mut x) =>

Your code works as expected when the types are trimmed down to &mut ExpensiveStruct:

match hm1.get_mut(&1) {
    Some(x) => match hm.entry(1) {
        Entry::Occupied(mut y) => if y.get().replace {
            mem::swap(x, y.get_mut());

(On the Playground)

Note that the ref mut is removed, because hm1.get_mut(&1) already returns an Option for a mutable reference, and the &mut is removed because y.get_mut() already returns a reference.

loganfsmyth
  • 156,129
  • 30
  • 331
  • 251