![](/img/trans.png)
[英]How to avoid mutable and immutable borrow with IntoParallelIterator bound
[英]How to avoid mutable borrow after immutable borrow?
我有一個結構(節點),它有 id:string、child_ids:set[string] 和 parent_ids:set[string]。 最初只有 id 和 parent_ids 被傳遞,我想稍后填寫 child_ids。
下面是我實現它的 Python 代碼。 它可以正常工作。
from dataclasses import dataclass
from typing import Dict, Set
@dataclass
class Node:
id: str
parent_ids: Set[str]
child_ids: Set[str]
NodeMap = Dict[str, Node]
def fill_child_ids(node_map: NodeMap):
for node_id, node in node_map.items():
for parent_id in node.parent_ids:
node_map[parent_id].child_ids.add(node_id)
if __name__ == "__main__":
node_map = {
"n1": Node("n1", set(), set()),
"n2": Node("n2", set(["n1"]), set()),
"n3": Node("n3", set(["n2"]), set()),
}
fill_child_ids(node_map)
assert node_map["n1"].child_ids == set(["n2"])
assert node_map["n2"].child_ids == set(["n3"])
現在我想在 Rust 中執行此操作。 我直接將Python代碼翻譯成Rust如下。
use std::collections::{HashMap, HashSet};
struct Node<'a> {
id: &'a str,
child_ids: HashSet<&'a str>,
parent_ids: HashSet<&'a str>,
}
impl<'a> Node<'a> {
fn new(id: &'a str, child_ids: HashSet<&'a str>, parent_ids: HashSet<&'a str>) -> Self {
Self {
id,
child_ids,
parent_ids,
}
}
}
type NodeMap<'a> = HashMap<&'a str, Node<'a>>;
fn fill_child_ids(node_map: &mut NodeMap) {
for (&node_id, node) in node_map.iter() {
for &parent_id in &node.parent_ids {
if let Some(parent_node) = node_map.get_mut(parent_id) {
parent_node.child_ids.insert(node_id);
}
}
}
}
fn main() {
let mut node_map = HashMap::new();
node_map.insert("n1", Node::new("n1", HashSet::new(), HashSet::new()));
node_map.insert("n2", Node::new("n2", HashSet::new(), HashSet::from(["n1"])));
node_map.insert("n3", Node::new("n3", HashSet::new(), HashSet::from(["n2"])));
fill_child_ids(&mut node_map);
assert_eq!(node_map["n1"].child_ids, HashSet::from(["n2"]));
assert_eq!(node_map["n2"].child_ids, HashSet::from(["n3"]));
}
但是這個不能編譯。
error[E0502]: cannot borrow `*node_map` as mutable because it is also borrowed as immutable
--> src\main.rs:24:40
|
22 | for (&node_id, node) in node_map.iter() {
| ---------------
| |
| immutable borrow occurs here
| immutable borrow later used here
23 | for &parent_id in &node.parent_ids {
24 | if let Some(parent_node) = node_map.get_mut(parent_id) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here
For more information about this error, try `rustc --explain E0502`.
如果我修改fill_child_ids
function 不直接循環遍歷 HashMap 的迭代器,而是通過鍵的副本,它工作。
fn fill_child_ids(node_map: &mut NodeMap) {
let node_ids: Vec<&str> = node_map.keys().map(|&node_id| node_id).collect();
for node_id in node_ids {
let parent_ids = node_map.get(node_id).unwrap().parent_ids.clone();
for parent_id in parent_ids {
if let Some(parent_node) = node_map.get_mut(parent_id) {
parent_node.child_ids.insert(node_id);
}
}
}
}
但是有沒有其他解決方案在迭代器上使用直接循環?
您可以在訪問它的迭代器之前克隆node_map
,您將獲得它的不可變副本,它現在是一個新的 object 並且不會與您在它下面的get_mut()
調用沖突:
fn fill_child_ids(node_map: &mut NodeMap) {
for (&node_id, node) in node_map.clone().iter() {
for &parent_id in &node.parent_ids {
if let Some(parent_node) = node_map.get_mut(parent_id) {
parent_node.child_ids.insert(node_id);
}
}
}
}
根據@user4815162342 的評論,我找到了一種使用RefCell
的方法。
type NodeMap<'a> = HashMap<&'a str, RefCell<Node<'a>>>;
fn fill_child_ids(node_map: &NodeMap) {
for node_ref in node_map.values() {
let node = node_ref.borrow();
for parent_id in node.parent_ids.iter() {
if let Some(parent_node) = node_map.get(parent_id) {
parent_node.borrow_mut().child_ids.insert(node.id);
}
}
}
}
關鍵是,HashMap 本身是不可變借用的,而我們通過borrow_mut
方法獲得成員的可變借用。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.