How can I convince the borrow checker to allow me

2019-02-25 01:14发布

问题:

The borrow checker beat me:

use std::collections::HashMap;

struct Cache {
    cache: Vec<HashMap<String, String>>,
}

impl Cache {
    fn get(&mut self, index: usize, key: String) -> String {
        let mut cache = &mut self.cache[index];
        match cache.get(&key) {
            Some(r) => {
                return r.clone();
            }
            None => {
                let r = "foo".to_string(); // something smart here
                cache.insert(key, r.clone());
                return r;
            }
        }
    }
}

What I get:

error[E0502]: cannot borrow `*cache` as mutable because it is also borrowed as immutable
  --> src/main.rs:16:17
   |
10 |         match cache.get(&key) {
   |               ----- immutable borrow occurs here
...
16 |                 cache.insert(key, r.clone());
   |                 ^^^^^ mutable borrow occurs here
...
19 |         }
   |         - immutable borrow ends here

How can I rewrite my code so that it compiles?

回答1:

Another approach is to use the entry interface. The only downside with this approach is that it (currently) doesn't use the BorrowFrom infrastructure that the get method uses, which makes it less flexible. In your case, that isn't a problem since get takes an owned key. The advantage of entry is that it only does one hash lookup, whereas using get forces you to do two lookups.

use std::collections::HashMap;

struct Cache {
    cache: Vec<HashMap<String, String>>,
}

impl Cache {
    fn get(&mut self, index: usize, key: String) -> String {
        self.cache[index]
            .entry(key)
            .or_insert_with(|| "foo".to_string())
            .clone()
    }
}


回答2:

The borrow checker sees cache.get as an immutable borrow, despite the fact that it is returning None. The easiest way to change your code is to move the insert out of the match, e.g.:

use std::collections::HashMap;

struct Cache {
    cache: Vec<HashMap<String, String>>,
}

impl Cache {
    fn get(&mut self, index: usize, key: String) -> String {
        let mut cache = &mut self.cache[index];
        match cache.get(&key) {
            Some(r) => {
                return r.clone();
            }
            None => (),
        }
        let r = "foo".to_string(); // something smart here
        cache.insert(key, r.clone());
        return r;
    }
}