如何处理 HashMap 中的每个值并选择性地拒绝某些值?

How to process every value in a HashMap and optionally reject some?

我想一个一个地处理 HashMap 中的值,同时可能会删除其中的一些值。

例如,我想做一个相当于:

use std::collections::HashMap;

fn example() {
    let mut to_process = HashMap::new();
    to_process.insert(1, true);

    loop {
        // get an arbitrary element
        let ans = to_process.iter().next().clone(); // get an item from the hash
        match ans {
            Some((k, v)) => {
                if condition(&k,&v) {
                    to_process.remove(&k);
                }
            }
            None => break, // work finished
        }
    }
}

但是编译失败:

error[E0502]: cannot borrow `to_process` as mutable because it is also borrowed as immutable
  --> src/lib.rs:12:17
   |
9  |         let ans = to_process.iter().next().clone();
   |                   ---------- immutable borrow occurs here
...
12 |                 to_process.remove(&k);
   |                 ^^^^^^^^^^^------^^^^
   |                 |          |
   |                 |          immutable borrow later used by call
   |                 mutable borrow occurs here

我知道我真的需要 https://github.com/rust-lang/rust/issues/27804(这是 HashSetHashMap 是一样的) 如果没有非可变和可变引用或使用不安全,我将无法实施所提供的解决方案。

有没有我遗漏的简单方法?

这看起来非常适合 Iterator::filter_map:

The closure must return an Option<T>. filter_map creates an iterator which calls this closure on each element. If the closure returns Some(element), then that element is returned. If the closure returns None, it will try again, and call the closure on the next element, seeing if it will return Some.

下面的process_and_maybe_add很简单,但是你明白了:

use std::collections::HashMap;

fn main() {
    let mut data = HashMap::new();
    data.insert(1, "a");
    data.insert(2, "b");
    data.insert(3, "c");

    let processed = data
        .into_iter()
        .filter_map(process_and_maybe_add)
        .collect::<HashMap<_, _>>();
    dbg!(processed);
}

fn process_and_maybe_add((k, v): (u32, &str)) -> Option<(u32, String)> {
    if k % 2 != 0 {
        Some((k + 100, v.to_owned() + v))
    } else {
        None
    }
}

注意 如果您需要在处理过程中更改密钥或向 HashMap 添加 kvps,请参阅@edwardw 的回答。否则...

使用HashMap::retain。您可以将处理函数更改为 return a bool 以指示是否保留该键值对。例如

let mut to_process: HashMap<u32, String> = HashMap::new();
to_process.insert(1, "ok".to_string());
to_process.insert(2, "bad".to_string());

to_process.retain(process);
    
fn process(k: &u32, v: &mut String) -> bool {
    // do stuff with k and v
    v == "ok"
}