英文:
Rust Borrow Checker Question: Don't know where immutable borrow happens that prevents compiling
问题
我想要从 Rust 的 HashSet 中删除随机元素,但我的实现似乎同时使用了可变借用和不可变借用。我不明白这是在哪里发生的,也不知道如何修复它。
以下是一个简单的程序,它从 HashSet 中随机选择一个元素,然后将其删除。但我得到了以下编译器错误:
错误[E0502]: 无法将 `set` 作为可变引用借用,因为它同时被不可变引用借用了
 --> src/main.rs:9:5
  |
8 |     let num = set.iter().choose(&mut rng).unwrap();
  |               ---------- 不可变引用在这里出现
9 |     set.remove(num);
  |     ^^^^------^^^^^
  |     |   |
  |     |   后续调用使用了不可变引用
  |     可变引用在这里出现
英文:
I want to remove random elements from a Rust HashSet, but my implementation seems to use a mutable borrow and immutable borrow at the same time. I don't understand where this is happening, or how to fix it.
use std::collections::HashSet;
use rand::seq::IteratorRandom;
fn main() {
    let mut set: HashSet<i32> = HashSet::new();
    set.insert(2); set.insert(3);
    let mut rng = rand::thread_rng();
    let num = set.iter().choose(&mut rng).unwrap();
    set.remove(num);
}
Here is a simple program that randomly chooses an element from a HashSet and then removes it. But I'm getting the following compiler error:
error[E0502]: cannot borrow `set` as mutable because it is also borrowed as immutable
 --> src/main.rs:9:5
  |
8 |     let num = set.iter().choose(&mut rng).unwrap();
  |               ---------- immutable borrow occurs here
9 |     set.remove(num);
  |     ^^^^------^^^^^
  |     |   |
  |     |   immutable borrow later used by call
  |     mutable borrow occurs here
I don't see how a borrow occurs in set.iter() or set.remove().
答案1
得分: 4
陷阱在于 iter() 返回对元素的引用,因此 num 本身也是一个引用。具体而言,Rust 中的情况如下:
let num: &i32 = set.iter().choose(&mut rng).unwrap();
现在,你有一个名为 num 的引用,它会与后来想要的可变引用发生冲突。
修复方法是在调用 remove() 之前消除引用:
let num: i32 = *set.iter().choose(&mut rng).unwrap(); // 引用已移除
set.remove(&num); // 转换回引用,但是变成了局部变量
这解决了冲突。
如果目标是删除某个随机元素,你还可以使用 retain 和一个函数来删除随机索引,这意味着不需要引用特定元素。
英文:
The trap here is that iter() returns references to elements, and as such, num itself is a reference. More specifically, this is what it looks like to Rust:
let num : &i32 = set.iter().choose(&mut rng).unwrap();
Where now you have a reference num that's lingering and in conflict with the mutable reference you later want.
The fix is to get rid of the reference before calling remove():
let num : i32 = *set.iter().choose(&mut rng).unwrap(); // Reference removed
set.remove(&num); // Convert back to reference, but to local variable
That resolves the conflict.
If the goal is to remove some random element, you could also remove a random index with retain and a function, which means there's no reference to a specific element needed.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论