How to update self based on reference of value fro

2019-07-14 18:12发布

问题:

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.

回答1:

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.



标签: rust