我应该如何重组我的图形代码,以避免“一次不能多次借用可变变量".错误? [英] How should I restructure my graph code to avoid an "Cannot borrow variable as mutable more than once at a time" error?
问题描述
我有一个可以成功编译的简单图形:
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:
- 使
add_node
不返回任何内容,或返回Key
,并提供一个单独的方法来获取给定键的&Node<T>
. - 将节点包装在
Rc<T>
或Arc<T>
.也就是说,不是list
是HashMap<Key, Node<T>>
,而是HashMap<Key, Rc<Node<T>>>
.您可以clone()
和Rc
或Arc
复制指针并增加引用计数.将一个副本存储在HashMap
中,并从add_node
返回另一个副本.- 如果您还需要在保留图元变异能力的同时对节点进行变异,则可能需要将
Rc
与Mutex
.
- Make
add_node
return nothing, or return theKey
, and provide a separate method to obtain a&Node<T>
given a key. - Wrap your nodes in
Rc<T>
orArc<T>
. That is, instead oflist
being aHashMap<Key, Node<T>>
, it would be aHashMap<Key, Rc<Node<T>>>
. You canclone()
anRc
orArc
to copy the pointer and increment the reference count; store one copy in theHashMap
and return the other copy fromadd_node
.- If you also need to mutate the nodes while retaining the ability to mutate the graph, you may need to combine
Rc
withRefCell
, orArc
withMutex
.
这篇关于我应该如何重组我的图形代码,以避免“一次不能多次借用可变变量".错误?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!
- If you also need to mutate the nodes while retaining the ability to mutate the graph, you may need to combine
- 如果您还需要在保留图元变异能力的同时对节点进行变异,则可能需要将