I can write the following function f
and the code compiles as I would expect:
use std::collections::HashMap;
struct A {
data: HashMap<u32, B>,
}
struct B {
val: Option<u32>,
}
impl A {
fn f(&mut self, key: u32) {
let data = &self.data[&key];
match data.val {
Some(value) => panic!("{}", value),
None => self.data.remove(&key),
};
}
}
Here key
is checked out first immutably &self.data[&key]
, and then again mutably (self.data.remove(&key)
), but the compiler allows this, likely because data
is never used after the mutable checkout.
However, if I then use a RefCell
rather than just a regular ref, I get a compile time error, even though the logic is otherwise (seemingly) the same:
use std::collections::HashMap;
use std::cell::RefCell;
struct A {
data: HashMap<u32, RefCell<B>>,
}
struct B {
val: Option<u32>,
}
impl A {
fn f(&mut self, key: u32) {
let data = self.data[&key].borrow();
match data.val {
Some(value) => panic!("{}", value),
None => self.data.remove(&key),
};
}
}
The error is:
error[E0502]: cannot borrow `self.data` as mutable because it is also borrowed as immutable
--> src/main.rs:17:21
|
14 | let data = self.data[&key].borrow();
| --------- immutable borrow occurs here
...
17 | None => self.data.remove(&key),
| ^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here
18 | };
19 | }
| - immutable borrow might be used here, when `data` is dropped and runs the destructor for type `Ref<'_, B>`
My guess is that the compiler isn't able to determine that I finished with data
, due to it being a RefCell
and thus checked at runtime, but if that's the case, is there any way I can 'check in' the value that I borrowed, since its no longer needed? Or is my only option to let it go out of scope? (Which is fine, for this small example, but aesthetically displeasing with larger ones.)
val
in an isolated scope? Something likematch { let data = self.data[&key].borrow(); data.val } { ... }
? – Martine