为什么HashMap有iter_mut()但是HashSet没有?

时间:2016-03-13 12:27:51

标签: rust standard-library idiomatic

在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并且最终试图通过各种分配排列来欺骗借用检查器,括起括号,但由于预期表达式的耦合,问题仍然存在。

2 个答案:

答案 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或者复制/克隆太昂贵,您可以将它们包装在RcArc中,并在使用{{{}}迭代它们时克隆它们3}}(克隆RcArc不克隆基础值,它只是克隆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));
}