Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
436 views
in Technique[技术] by (71.8m points)

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

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<String, String>) {
    for (key, value) in map {
        println!("{} / {}", key, value);
        map.remove(key);
    }
}

fn main() {}

Here's the compiler error:

error[E0382]: use of moved value: `*map`
 --> src/main.rs:6:9
  |
4 |     for (key, value) in map {
  |                         --- value moved here
5 |         println!("{} / {}", key, value);
6 |         map.remove(key);
  |         ^^^ value used here after move
  |
  = note: move occurs because `map` has type `&mut std::collections::HashMap<std::string::String, std::string::String>`, which does not implement the `Copy` trait

Why it is trying to move a reference? From the documentation, I didn't think moving/borrowing applied to references.

question from:https://stackoverflow.com/questions/45724517/how-to-iterate-through-a-hashmap-print-the-key-value-and-remove-the-value-in-ru

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

There are at least two reasons why this is disallowed:

  1. You would need to have two concurrent mutable references to map — one held by the iterator used in the for loop and one in the variable map to call map.remove.

  2. You have references to the key and the value within the map when trying to mutate the map. If you were allowed to modify the map in any way, these references could be invalidated, opening the door for memory unsafety.

A core Rust principle is Aliasing XOR Mutability. You can have multiple immutable references to a value or you can have a single mutable reference to it.

I didn't think moving/borrowing applied to references.

Every type is subject to Rust's rules of moving as well as mutable aliasing. Please let us know what part of the documentation says it isn't so we can address that.

Why it is trying to move a reference?

This is combined of two parts:

  1. You can only have a single mutable reference, so mutable references don't implement the Copy trait
  2. for loops take the value to iterate over by value

When you call for (k, v) in map {}, the ownership of map is transferred to the for loop and is now gone.


I'd perform an immutable borrow of the map (&*map) and iterate over that. At the end, I'd clear the whole thing:

fn do_it(map: &mut HashMap<String, String>) {
    for (key, value) in &*map {
        println!("{} / {}", key, value);
    }
    map.clear();
}

remove every value with a key that starts with the letter "A"

I'd use HashMap::retain:

fn do_it(map: &mut HashMap<String, String>) {
    map.retain(|key, value| {
        println!("{} / {}", key, value);

        !key.starts_with("a")
    })
}

This guarantees that key and value no longer exist when the map is actually modified, thus any borrow that they would have had is now gone.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...