【问题标题】:How should I restructure my graph code to avoid an "Cannot borrow variable as mutable more than once at a time" error?我应该如何重组我的图形代码以避免“不能一次多次借用变量作为可变变量”错误?
【发布时间】:2016-04-12 07:02:11
【问题描述】:

我有一个成功编译的简单图表:

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
    }
}

但是使用时编译失败:

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() 使用&amp;mut self。在所有类似的问题中,一般的答案是“重组你的代码”。我不明白我该怎么办?我应该如何重构这段代码?

【问题讨论】:

  • 您的代码示例过于简单,我们无法为您提供好的建议。您可以简单地将let n1 = graph.add_node(111); 放入一个块中,然后您的代码就可以工作了,但我很确定这不是您想要的。
  • @ker 这不是例子。是培训项目。我想创建一个简单的图表。但是我不能给它添加一些节点。
  • 您不能简单地返回Key 而不是&amp;Node 吗?除了创建边缘的关键之外,您不需要任何东西

标签: rust lifetime borrowing


【解决方案1】:

通过从add_node 返回&amp;Node&lt;T&gt;,您实际上锁定了整个Graph&lt;T&gt; 对象,因为您是从它借用的。并且有充分的理由;尝试运行这个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;
        }
    }
}

这是该程序可能的输出:

0x7f86c6c302e0 0x7f86c6c3a6e0 (29 inserts)

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

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

知道了这一切,应该清楚add_node 不应该返回&amp;Node&lt;T&gt;。以下是一些替代方案:

  • 使add_node 不返回任何内容,或者返回Key,并提供一个单独的方法来获取给定密钥的&amp;Node&lt;T&gt;。
  • 将您的节点包装在Rc&lt;T&gt; 或Arc&lt;T&gt; 中。也就是说,list 不是HashMap&lt;Key, Node&lt;T&gt;&gt;,而是HashMap&lt;Key, Rc&lt;Node&lt;T&gt;&gt;&gt;。您可以通过clone() 和Rc 或Arc 复制指针并增加引用计数;将一个副本存储在HashMap 并从add_node 返回另一个副本。
    • 如果您还需要改变节点同时保留改变图形的能力,您可能需要将Rc 与RefCell 或Arc 与Mutex 结合使用。

【讨论】:

    【解决方案2】:

    我使用std::rc::Rc解决了问题:

    use std::collections::HashMap;
    use std::rc::Rc;
    
    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, Rc<Node<T>>>, // <-- Changed
        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) -> Rc<Node<T>> {
            // <-- Changed
            let key = self.get_next_key();
            let node = Rc::new(Node::new(key, value)); // <-- Changed
            self.list.insert(key, node.clone()); // <-- Changed
            self.map.insert(key, HashMap::new());
            node
        }
    
        fn get_next_key(&mut self) -> Key {
            let key = self.next_key;
            self.next_key += 1;
            key
        }
    }
    
    fn main() {
        let mut graph = Graph::<i32>::new();
        let n1 = graph.add_node(111);
        let n2 = graph.add_node(222);
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-01-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-02-24
      • 1970-01-01
      • 1970-01-01
      • 2015-10-16
      相关资源
      最近更新 更多