I'm trying to look up the value of an element (B) in a BTreeMap
, from an element (A) that comes from the same map and then mutate the value of A with the value of B.
I know I can use a RefCell
to avoid the "cannot borrow X as immutable because X is also borrowed as mutable" error, but I wanted to know if there was a more idiomatic, safe way to do this.
I've thought of trying to store pointers to the other elements, but that doesn't work because the memory locations of elements change move as one adds elements to the BTreeMap
, so it's unsafe and wrong.
Is there a better approach to this kind of pattern?
Initial try
use std::collections::BTreeMap;
struct Map(BTreeMap<String, Foo>);
impl Map {
fn new() -> Self {
Map(BTreeMap::new())
}
fn calc_node(&mut self, name: &str) {
self.0.get_mut(name).unwrap().calc_value(self)
}
}
struct Foo {
name: String,
v: i32,
lookup: String,
}
impl Foo {
fn new(name: String, value: i32, lookup: String) -> Self {
Self {
name,
v: value,
lookup,
}
}
fn calc_value(&mut self, map: &Map) {
self.v = map.0.get(&self.lookup).unwrap().v * 2 // really what I'm trying to do
}
}
fn main() {
let mut map = Map::new();
map.0.insert(
"a".to_string(),
Foo::new("a".to_string(), 1, "b".to_string()),
);
map.0.insert(
"b".to_string(),
Foo::new("b".to_string(), 2, "a".to_string()),
);
println!("{:?}", map.0.get("a").unwrap().v);
map.calc_node("a");
println!("{:?}", map.0.get("a").unwrap().v);
}
error[E0502]: cannot borrow `*self` as immutable because it is also borrowed as mutable
--> src/main.rs:11:50
|
11 | self.0.get_mut(name).unwrap().calc_value(self)
| ------ ---------- ^^^^ immutable borrow occurs here
| | |
| | mutable borrow later used by call
| mutable borrow occurs here
Using RefCell
use std::cell::RefCell;
use std::collections::BTreeMap;
struct Map(BTreeMap<String, RefCell<Foo>>);
impl Map {
fn new() -> Self {
Map(BTreeMap::new())
}
fn calc_node(&self, name: &str) {
self.0.get(name).unwrap().borrow_mut().calc_value(self)
}
}
struct Foo {
name: String,
v: i32,
lookup: String,
}
impl Foo {
fn new(name: String, value: i32, lookup: String) -> Self {
Self {
name,
v: value,
lookup,
}
}
fn calc_value(&mut self, map: &Map) {
self.v = map.0.get(&self.lookup).unwrap().borrow().v * 2
}
}
fn main() {
let mut map = Map::new();
map.0.insert(
"a".to_string(),
RefCell::new(Foo::new("a".to_string(), 1, "b".to_string())),
);
map.0.insert(
"b".to_string(),
RefCell::new(Foo::new("b".to_string(), 2, "a".to_string())),
);
println!("{:?}", map.0.get("a").unwrap().borrow().v);
map.calc_node("a");
println!("{:?}", map.0.get("a").unwrap().borrow().v);
}
I get the correct result:
1
4
RefCell
s have a run-time cost and I would like to ideally be able to access referenced elements as quickly as possible.