[Solved] Noob question: Book section about HashMap


I am a newbie to rust (and programming) and currently going through the HashMap section in section 8.3 of the rust book and there is this example code snippet:

let teams = vec![String::from("Blue"), String::from("Yellow")];
let initial_scores = vec![10, 50];

let scores: HashMap<_, _> = teams.iter().zip(initial_scores.iter()).collect();

I understand that this creates two vectors and puts them into scores as key/value pairs of a HashMap. I wanted to change a value as shown in a code snippet further down, but I am running into a type error.

First I changed let scores: ... to let mut scores: ..., then I tried to use .insert() to change a value, like this:

scores.insert(String::from("Blue"), 25);

The compiler at this point tells me that it expected &std::string::String and &{integer}, but I do not understand why I need to use borrowed values here. Why are teams and initial_scores not moved into the HashMap, or is there some other reason why I need to use borrowed values in the .insert() bit?

Thanks in advance!

It's because .iter(). Its signature is fn iter(&self) -> impl Iterator<Item=&T>, so for teams which is Vec<String> it yields iterator of &String. Try code like below:

let scores: HashMap<String, i32> = teams.into_iter()
1 Like

Thank you, that does work! If I understand the docs correctly, .into_iter() is basically .iter() but it moves the values instead of borrowing them?

And one additional question: Why do I not need .zip(initial_scores.into_iter())? The signature of .zip() looks like I need to pass an iterator there, not just the vector. Am I missing something?

That second question just resolved itself, quote from the .zip docs:

Since the argument to zip() uses IntoIterator, we can pass anything that can be converted into an Iterator, not just an Iterator itself.

So I guess the "iter()" in .zip(initial_scores.iter()) only served the purpose of turning that into borrowed values too, since .zip() uses into_iter() and would otherwise move the value.


Another question as I am going through this chapter again:

This code snippet in the book has been changed since my initial post, replacing .iter() with .into_iter() at every point. The snippet from the book now looks like this:

let mut scores: HashMap<_, _> =

As mentioned in my post above, the second into_iter() is not necessary as .zip() uses IntoIterator by default. Is there a reason why it should be included anyway?

As you've said, it's entirely equivalent to not having .into_iter().

My best guess is that this makes the code more symmetric, and thus easier to read. teams.into_iter().zip(initial_scores) is correct, but it might take an extra second to read it and realize that zip does the into_iter() operation automatically. On the other hand, teams.into_iter().zip(initial_scores.into_iter()) is entirely obvious, and the symmetry of into_iter()s helps point out that zip is the most important operation.

In any case, it's either a stylistic choice, or a missed opportunity - I could also see someone replacing all the .iter() calls with .into_iter() without thinking too much.

Yeah I could only come up with clarity as a possible reason, that makes sense :slight_smile:

What do you mean by this? Do you feel like this example would be better with .iter() or .into_iter()?

Never mind the last part, I understood it now :smile:

Thanks for your input!

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.