我应该如何重组我的图形代码,以避免“一次不能多次借用可变变量".错误? [英] How should I restructure my graph code to avoid an "Cannot borrow variable as mutable more than once at a time" error?

查看:147
本文介绍了我应该如何重组我的图形代码,以避免“一次不能多次借用可变变量".错误?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个可以成功编译的简单图形:

I have a simple graph that successfully compiles:

use std::collections::HashMap;

type Key = usize;
type Weight = usize;

#[derive(Debug)]
pub struct Node<T> {
    key: Key,
    value: T,
}
impl<T> Node<T> {
    fn new(key: Key, value: T) -> Self {
        Node {
            key: key,
            value: value,
        }
    }
}

#[derive(Debug)]
pub struct Graph<T> {
    map: HashMap<Key, HashMap<Key, Weight>>,
    list: HashMap<Key, Node<T>>,
    next_key: Key,
}
impl<T> Graph<T> {
    pub fn new() -> Self {
        Graph {
            map: HashMap::new(),
            list: HashMap::new(),
            next_key: 0,
        }
    }
    pub fn add_node(&mut self, value: T) -> &Node<T> {
        let node = self.create_node(value);
        node
    }

    fn create_node(&mut self, value: T) -> &Node<T> {
        let key = self.get_next_key();
        let node = Node::new(key, value);
        self.list.insert(key, node);
        self.map.insert(key, HashMap::new());
        self.list.get(&key).unwrap()
    }

    fn get_next_key(&mut self) -> Key {
        let key = self.next_key;
        self.next_key += 1;
        key
    }
}

但是当我使用它时,它无法编译:

But it fails to compile when I use it:

fn main() {
    let mut graph = Graph::<i32>::new();
    let n1 = graph.add_node(111);
    let n2 = graph.add_node(222);
}

错误:

error[E0499]: cannot borrow `graph` as mutable more than once at a time
  --> src/main.rs:57:14
   |
56 |     let n1 = graph.add_node(111);
   |              ----- first mutable borrow occurs here
57 |     let n2 = graph.add_node(222);
   |              ^^^^^ second mutable borrow occurs here
58 | }
   | - first borrow ends here

我看过所有类似的问题.我知道那是失败的,因为方法Graph::add_node()使用&mut self.在所有类似的问题中,通常的答案是重组代码".我不明白该怎么办?我应该如何重组此代码?

I have seen all similar questions. I know that is failing because method Graph::add_node() uses &mut self. In all similar questions, the general answer is "restructure your code". I can't understand what should I do? How should I restructure this code?

推荐答案

通过从add_node返回&Node<T>,您可以有效地锁定整个Graph<T>对象,因为您是在借用它.并且有充分的理由;尝试运行此main:

By returning a &Node<T> from add_node, you are effectively locking the whole Graph<T> object, because you're borrowing from it. And for a good reason; try running this main:

fn main() {
    let mut graph = Graph::<i32>::new();
    let n1 = graph.add_node(111) as *const _;
    let mut inserts = 0;
    loop {
        inserts += 1;
        graph.add_node(222);
        let n1bis = graph.list.get(&0).unwrap() as *const _;
        if n1 != n1bis {
            println!("{:p} {:p} ({} inserts)", n1, n1bis, inserts);
            break;
        }
    }
}

以下是该程序的可能输出:

Here's a possible output from this program:

0x7f86c6c302e0 0x7f86c6c3a6e0 (29 inserts)

该程序添加第一个节点并将其地址存储为原始指针(原始指针没有生命周期参数,因此释放了对Graph的借用).然后,它一次添加一个更多的节点,然后再次获取第一个节点的地址.如果第一个节点的地址发生更改,它将打印两个地址以及插入到图中的其他节点数.

This program adds a first node and stores its address as a raw pointer (raw pointers don't have a lifetime parameter, so the borrow on Graph is released). Then, it adds more nodes, one at a time, then fetches the address of the first node again. If the address of the first node changed, it prints both addresses as well as the number of additional nodes that were inserted into the graph.

HashMap使用随机哈希,因此每次执行时插入的次数将有所不同.但是,最终需要重新分配内存以存储更多条目,因此最终,映射中节点的地址发生了变化.如果您在这种情况发生后尝试取消引用旧指针(例如n1),那么您将访问已释放的内存,这可能会返回垃圾数据或导致错误(通常是分段错误).

HashMap uses a randomized hash, so the number of inserts will vary on each execution. However, it will eventually need to reallocate memory to store more entries, so eventually, the address of the nodes in the map change. If you tried to dereference an old pointer (such as n1) after this happened, then you'd be accessing freed memory, which may return garbage data or cause an error (usually a segmentation fault).

了解了所有这些内容后,很显然add_node不应返回&Node<T>.以下是一些替代方法:

Knowing all this, it should be clear that add_node should not return a &Node<T>. Here are a few alternatives:

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆