从同一个HashMap中借用两个可变值

时间:2017-12-12 13:28:51

标签: rust

我有以下代码:

use std::collections::{HashMap, HashSet};

fn populate_connections(
    start: i32,
    num: i32,
    conns: &mut HashMap<i32, HashSet<i32>>,
    ancs: &mut HashSet<i32>,
) {
    let mut orig_conns = conns.get_mut(&start).unwrap();
    let pipes = conns.get(&num).unwrap();

    for pipe in pipes.iter() {
        if !ancs.contains(pipe) && !orig_conns.contains(pipe) {
            ancs.insert(*pipe);
            orig_conns.insert(*pipe);
            populate_connections(start, num, conns, ancs);
        }
    }
}

fn main() {}

逻辑不是很重要,我正在尝试创建一个本身并遍历管道的函数。

我的问题是这不能编译:

error[E0502]: cannot borrow `*conns` as immutable because it is also borrowed as mutable
  --> src/main.rs:10:17
   |
9  |     let mut orig_conns = conns.get_mut(&start).unwrap();
   |                          ----- mutable borrow occurs here
10 |     let pipes = conns.get(&num).unwrap();
   |                 ^^^^^ immutable borrow occurs here
...
19 | }
   | - mutable borrow ends here

error[E0499]: cannot borrow `*conns` as mutable more than once at a time
  --> src/main.rs:16:46
   |
9  |     let mut orig_conns = conns.get_mut(&start).unwrap();
   |                          ----- first mutable borrow occurs here
...
16 |             populate_connections(start, num, conns, ancs);
   |                                              ^^^^^ second mutable borrow occurs here
...
19 | }
   | - first borrow ends here

我不知道如何让它发挥作用。一开始,我正在尝试将HashSet存储在HashMaporig_connspipes)中。

Rust不会让我同时拥有可变和不可变的变量。我有点困惑,因为这将是完全不同的对象,但我想如果&start == &num,那么我会对同一个对象有两个不同的引用(一个是可变的,一个是不可变的)。 / p>

好的,但是我怎么能实现这个目标呢?我想迭代一个HashSet并阅读和修改其他的HashSet。我们假设它们不会是$("#btn1").click(myFunction);

2 个答案:

答案 0 :(得分:9)

如果您可以更改数据类型和功能签名,则可以使用RefCell创建interior mutability

use std::cell::RefCell;
use std::collections::{HashMap, HashSet};

fn populate_connections(
    start: i32,
    num: i32,
    conns: &HashMap<i32, RefCell<HashSet<i32>>>,
    ancs: &mut HashSet<i32>,
) {
    let mut orig_conns = conns.get(&start).unwrap().borrow_mut();
    let pipes = conns.get(&num).unwrap().borrow();

    for pipe in pipes.iter() {
        if !ancs.contains(pipe) && !orig_conns.contains(pipe) {
            ancs.insert(*pipe);
            orig_conns.insert(*pipe);
            populate_connections(start, num, conns, ancs);
        }
    }
}

fn main() {}

请注意,如果start == num,线程将会发生混乱,因为这是尝试同时具有对同一HashSet的可变和不可变访问。

RefCell

的安全替代方案

根据您的确切数据和代码需求,您还可以使用Cellatomics之类的类型。它们的内存开销低于RefCell,对codegen的影响很小。

在多线程案例中,您可能希望使用MutexRwLock

答案 1 :(得分:3)

如果可以保证两个索引不同,则可以使用不安全的代码并避免内部可变性:

HashMap

此代码尝试多加注意。一个断言强制了以下事实:两个键是不同的,并且我们显式地将生存期添加到返回的变量中。

在盲目使用此解决方案之前,您应该了解不安全代码的细微差别。


请注意,此功能不会试图解决原始问题,这比验证两个索引不相交要复杂得多。原始问题要求:

  • 跟踪三个不相交的借用,其中两个是可变的,另一个是不可变的。
  • 跟踪递归调用
    • 不得以任何可能导致调整大小的方式修改RefCell,这会使先前级别中的任何现有引用无效。
    • 不得为上一级的任何引用添加别名。

使用类似#include <iostream> #include <fstream> #include <iomanip> #include <string> using namespace std; int main() { string item = ""; ifstream fin; double tgross = 0; double tnet = 0; double hourly; double hours; double taxrate; double net; string fileName = "payroll.txt"; fin.open("payroll.txt"); if(!fin.is_open()) { void instructions() { cout << "This payroll program calculates an individual employee pay and"; cout << "\ncompany totals using data from a data file payroll.txt.\n"; cout << "\n\nA payroll report showing payroll information "; cout << " is displayed.\n\n"; } void reportTitle() { cout << setprecision(2) << fixed << showpoint << left << setw(20) << "Employee" << setw(10) << "Hourly" << setw(10) << "Hours" << setw(10) << "Tax" << setw(10) << "Gross" << setw(10) << "Net" << endl; cout << setw(20) << "Name" << setw(10) << "Rate" << setw(10) << "Worked" << setw(10) << "Rate" << setw(10) << "Amount" << setw(10) << "Amount" << endl; } } while(!fin.eof()) { getline(fin,item,'#'); fin >> hourly >> hours >> taxrate; double calculateGross(double hours, double hourly) double calculateNet(double grosspay, double netpercent) { return grosspay - grosspay*netpercent/100.0; } void displayEmployeeInfo(const string &, double, double, double, double, double) { tgross += grosspay; tnet += net; } } void totalAmounts (double tgross, double tnet) { cout << "Totals" << setprecision(2) << fixed << showpoint << right << setw(50) << tgross << setw(10) << tnet << endl; } fin.close(); } 之类的方法来确保您不会触发内存不安全的情况更为简单。

相关问题