use std::collections::HashMap;
use std::collections::hash_map::Entry::*;
struct A {
map: HashMap<String, String>,
i: i32
}
impl A {
fn test(&mut self) {
match self.map.get("abc") {
None => {},
Some(x) => self.trigger(&x)
}
}
fn trigger(&mut self, x: &str) {
self.i += 1;
}
}
The code doesn't work because self.trigger
borrows self
mutably, while self.map.get
is keeping an immutable borrow of self
in scope.
Is there any way to make it work, given that I can make sure in the trigger
I don't modify self.map
?
I cannot make trigger
borrow self
immutably, as in Can I borrow self immutably for self.callbacks: Vec<Box<FnMut>>?
I'm using rustc 1.19.0-nightly.
The problem here is that the borrow checker does not know that trigger
only changes self.i
. As far as the borrow checker knows, it might also change self.map
, which would be unsafe.
The solution is to tell the borrow checker more about what trigger
changes.
One way to do this is by moving everything that trigger
needs to borrow mutablely into its own struct, and then implement trigger for that struct:
use std::collections::HashMap;
use std::collections::hash_map::Entry::*;
struct TriggerThing {
i: i32
}
impl TriggerThing {
fn trigger(&mut self, _: &HashMap<String, String>, x: &str) {
self.i += 1;
}
}
struct A {
map: HashMap<String, String>,
trigger_thing: TriggerThing,
}
impl A {
fn test(&mut self) {
// Its fine to have a immutable borrow of self.map
// and a mutable borrow of self.trigger_thing at the
// same time, since borrowing self.trigger_thing does not
// imply a mutable borrow of self.map.
if let Some(x) = self.map.get("abc") {
// Notice that we can still give self.trigger_thing
// access to self.map, as long as we only
// give it an immutable reference
self.trigger_thing.trigger(&self.map, &x)
}
}
}
see Rust Book: If Let if you haven't seen the awesome if let
syntax it before.