在Rust中为HashMap
但不是HashSet
提供iter_mut
函数的设计原理是什么?
滚动自己(假设甚至可以这样做)会是一种失礼吗?
有一个可以缓解导致
的情况此前借用
X
;不可变借入阻止 随后的移动或X
的可变借款,直到借款结束
An extremely convoluted example (Gist)没有显示参数传递的原因。有一个简短的评论解释痛点:
use std::collections::HashSet;
fn derp(v: i32, unprocessed: &mut HashSet<i32>) {
if unprocessed.contains(&v) {
// Pretend that v has been processed
unprocessed.remove(&v);
}
}
fn herp(v: i32) {
let mut unprocessed: HashSet<i32> = HashSet::new();
unprocessed.insert(v);
// I need to iterate over the unprocessed values
while let Some(u) = unprocessed.iter().next() {
// And them pass them mutably to another function
// as I will process the values inside derp and
// remove them from the set.
//
// This is an extremely convoluted example but
// I need for derp to be a separate function
// as I will employ recursion there, as it is
// much more succinct than an iterative version.
derp(*u, &mut unprocessed);
}
}
fn main() {
println!("Hello, world!");
herp(10);
}
声明
while let Some(u) = unprocessed.iter().next() {
是一个不可变的借用,因此
derp(*u, &mut unprocessed);
是不可能的,因为未经处理的人不能互相借用。不可变借用直到while循环结束才结束。
我试图使用this as reference并且最终试图通过各种分配排列来欺骗借用检查器,括起括号,但由于预期表达式的耦合,问题仍然存在。
答案 0 :(得分:9)
你必须考虑HashSet
实际上是什么。您从IterMut
获得的HashMap::iter_mut()
仅在值部分可变:(&key, &mut val)
,((&'a K, &'a mut V)
)
HashSet
基本上是HashMap<T, ()>
,因此实际值是键,如果您要修改键,则必须更新它们的哈希值,否则您将获得无效{{1} }。
答案 1 :(得分:2)
如果您的HashSet
包含Copy
类型,例如i32
,您可以处理该值的副本,以便尽早释放HashSet
上的借用。为此,您需要消除while let
表达式中绑定的所有借位。在原始代码中,u
的类型为&i32
,并且它会继续从unprocessed
借用,直到循环结束。如果我们将模式更改为Some(&u)
,那么u
的类型为i32
,不会借用任何内容,因此我们可以随意使用unprocessed
fn herp(v: i32) {
let mut unprocessed: HashSet<i32> = HashSet::new();
unprocessed.insert(v);
while let Some(&u) = unprocessed.iter().next() {
derp(u, &mut unprocessed);
}
}
如果类型不是Copy
或者复制/克隆太昂贵,您可以将它们包装在Rc
或Arc
中,并在使用{{{}}迭代它们时克隆它们3}}(克隆Rc
或Arc
不克隆基础值,它只是克隆Rc
指针并递增引用计数器。)
use std::collections::HashSet;
use std::rc::Rc;
fn derp(v: &i32, unprocessed: &mut HashSet<Rc<i32>>) {
if unprocessed.contains(v) {
unprocessed.remove(v);
}
}
fn herp(v: Rc<i32>) {
let mut unprocessed: HashSet<Rc<i32>> = HashSet::new();
unprocessed.insert(v);
while let Some(u) = unprocessed.iter().cloned().next() {
// If you don't use u afterwards,
// you could also pass if by value to derp.
derp(&u, &mut unprocessed);
}
}
fn main() {
println!("Hello, world!");
herp(Rc::new(10));
}