簡體   English   中英

不可變借用后如何避免可變借用?

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM