首页 > 解决方案 > 有没有办法使用地图中元素的值来更新另一个没有内部可变性的元素?

问题描述

BTreeMap我正在尝试从来自同一映射的元素(A)中查找元素(B)的值,然后将 A 的值与 B 的值进行变异。

我知道我可以使用 aRefCell来避免“不能将 X 作为不可变借用,因为 X 也作为可变借用”错误,但我想知道是否有更惯用、更安全的方法来做到这一点。

我曾想过尝试存储指向其他元素的指针,但这不起作用,因为元素的内存位置会随着向 中添加元素而移动BTreeMap,因此这是不安全和错误的。

这种模式有更好的方法吗?

初步尝试

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

使用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);
}

我得到正确的结果:

1
4

RefCells 有运行时成本,理想情况下,我希望能够尽快访问引用的元素。

标签: rustborrow-checker

解决方案


推荐阅读