我应该如何重构我的图形代码以避免"不能一次多次将变量借入变量#34;错误?

时间:2016-04-12 07:02:11

标签: rust lifetime borrowing

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

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()使用&mut self。在所有类似的问题中,一般的答案是&#34;重构您的代码&#34;。我无法理解我该怎么办?我该如何重组此代码?

2 个答案:

答案 0 :(得分:4)

&Node<T>返回add_node,您实际上锁定了整个Graph<T>对象,因为您正在从中借用它。并且有充分的理由;尝试运行此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不应该返回&Node<T>。以下是一些替代方案:

  • add_node不返回任何内容,或返回Key,并提供单独的方法来获取给定密钥的&Node<T>
  • 将您的节点包裹在Rc<T>Arc<T>中。也就是说,list不是HashMap<Key, Node<T>>,而是HashMap<Key, Rc<Node<T>>>。您可以clone() RcArc复制指针并增加引用计数;在HashMap中存储一个副本,并从add_node返回另一个副本。
    • 如果您还需要改变节点,同时保留变异图的功能,则可能需要将RcRefCellArcMutex合并。

答案 1 :(得分:1)

我使用 int[] input = {4,7,2,6,3,5,1,0} int cmpt = 0 ; int totalFalses ; final int[] posi =new int[31]; Arrays.parallelSetAll(posi, i -> i ); OptionalInt max = Arrays.stream(input).parallel().max(); int d_max = RadixSort.log2nlz(max.getAsInt()); for( int p : posi) { System.out.println("=========new loop =========="); int[] b = Arrays.stream(input).parallel().map(x -> x >> p & 1 ).toArray(); //take into account the bit. System.out.println("b= " + Arrays.toString(b) ); //reverse the bit and make a exclusive prefix sum ! int[] e_f = Arrays.stream(input).parallel().map(x -> ~x >> p & 1 ).toArray(); totalFalses = e_f[e_f.length-1] ; //Doing the prefix sum exclusive with a function created. The one from java is inclusive. ParallelPrefixSum PPS = new ParallelPrefixSum(e_f , 0, e_f.length-1,RadixSort.log2nlz(e_f.length-1)) ; try { PPS.scan(); } catch (Exception e) { e.printStackTrace(); } totalFalses += e_f[e_f.length-1] ; System.out.println("e_f= " + Arrays.toString(e_f) ); int totalFalsetmp = totalFalses; //create and compute t arrays int[] t = new int[input.length] ; Arrays.parallelSetAll(t, i -> i - e_f[i] + totalFalsetmp ) ; System.out.println("t= " + Arrays.toString(t) ); //create and compute d arrays int[] d = new int[input.length] ; Arrays.parallelSetAll(d, i -> (b[i]==1)?t[i]:e_f[i]) ; System.out.println("d= " + Arrays.toString(d) ); //place element in the rigth order int[] tmp = input ; int[] output = new int[input.length]; IntStream.range(0, input.length).forEach(i -> output[d[i]] = tmp[i]); input = output ; if(cmpt >= d_max){ break ; } cmpt ++ ; } 解决了问题:

std::rc::Rc