How to iterate through a Hashmap, print the key/value and remove the value in Rust?

后端 未结 2 897
我在风中等你
我在风中等你 2021-01-07 17:08

This should be a trivial task in any language. This isn\'t working in Rust.

use std::collections::HashMap;

fn do_it(map: &mut HashMap

        
2条回答
  •  再見小時候
    2021-01-07 17:45

    This should be a trivial task in any language.

    Rust is preventing you from mutating the map while you are iterating over it. In most languages this is allowed, but often the behaviour is not well-defined, and removal of the item can interfere with the iteration, compromising its correctness.

    Why it is trying to move a reference?

    HashMap implements IntoIterator, so your loop is equivalent to:

    for (key, value) in map.into_iter() {
        println!("{} / {}", key, value);
        map.remove(key);
    }
    

    If you look at the definition of into_iter, you'll see that it takes self, not &self or &mut self. Your variable map is a reference, so it is implicitly dereferenced to get at self, which is why the error says that *map has been moved.

    The API is intentionally built that way so that you can't do anything dangerous while looping over a structure. Once the loop is complete, the ownership of the structure is relinquished and you can use it again.

    One solution is to keep track of the items you intend to remove in a Vec and then remove them afterwards:

    fn do_it(map: &mut HashMap) {
        let mut to_remove = Vec::new();
        for (key, value) in &*map {
            if key.starts_with("A") {
                to_remove.push(key.to_owned());
            }
        }
        for key in to_remove.iter() {
            map.remove(key);
        }
    }
    

    You may also use an iterator to filter the map into a new one. Perhaps something like this:

    fn do_it(map: &mut HashMap) {
        *map = map.into_iter().filter_map(|(key, value)| {
            if key.starts_with("A") {
                None
            } else {
                Some((key.to_owned(), value.to_owned()))
            }
        }).collect();
    }
    

    But I just saw Shepmaster's edit - I had forgotten about retain, which is better. It's more concise and doesn't do unnecessary copying as I have done.

提交回复
热议问题