下面的代码:
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
存储在一个HashMap
中(orig_conns
和pipes
)。
Rust不允许我同时拥有可变变量和不可变变量,我有点困惑,因为这将是完全不同的对象,但我猜如果&start
== &num
,那么我将拥有对同一对象的两个不同引用(一个可变,一个不可变)。
这是可以的,但是我怎么才能做到呢?我想迭代一个HashSet
,读取并修改另一个。让我们假设它们不是相同的HashSet
。
2条答案
按热度按时间6ojccjat1#
如果可以更改数据类型和函数签名,则可以使用
RefCell
创建interior mutability:请注意,如果为
start == num
,则线程将死机,因为这是对同一HashSet
同时进行可变和不可变访问的尝试。RefCell
的安全替代方案根据你对数据和代码的需求,你也可以使用
Cell
或者atomics类型,这些类型比RefCell
的内存开销要小,对代码生成的影响也很小。在多线程的情况下,您可能希望使用
Mutex
或RwLock
。xpcnnkqh2#
使用
hashbrown::HashMap
如果您可以切换到使用hashbrown,则可以使用类似
get_many_mut
的方法:由于hashbrown是标准库hashmap的动力,因此在Rust的夜间版本中也可以使用
HashMap::get_many_mut
。不安全代码
如果你能保证你的两个索引是不同的,你就可以使用不安全的代码并避免内部可变性:
类似的代码可以在multi_mut这样的库中找到。
这段代码非常谨慎,Assert强制两个值是不同的指针,然后再将它们转换回可变引用,并且我们显式地为返回的变量添加了生存期。
在盲目使用这个解决方案之前,你应该了解不安全代码的细微差别。值得注意的是,previous versions of this answer是不正确的。感谢@oberien发现了这个问题的原始实现中的不合理之处,并提出了一个修复方案。This playground演示了纯粹安全的Rust代码如何导致旧代码导致内存不安全。
此解决方案的增强版本可以接受键数组并返回值数组:
然而,确保所有传入密钥都是唯一的变得更加困难。
注意,这个函数并不试图解决原始问题,原始问题比验证 * 两个 * 索引不相交要复杂得多。原始问题要求:
HashMap
,这将使上一级别的任何现有引用无效。使用
RefCell
之类的东西是一种 * 简单得多 * 的方法,可以确保您不会触发内存不安全。