We have a HashMap, over which we iterate and map to replace the values, but are running into an issue collecting that back to a new HashMap with different value type.
value of type `std::collections::HashMap<std::string::String, std::string::String>`
cannot be built from `std::iter::Iterator<Item=(&std::string::String, std::string::String)>`
What we are doing essentially boils down to this:
let old: HashMap<String, Value> = some_origin();
let new: HashMap<String, String> = old.iter().map(|(key, value)| {
return (key, some_conversion(value));
}).collect();
The same iterator type is also returned (and not collectable), if one zips two iterators, e.g. in this case zipping key, and the map that only returns the converted value.
new = old.keys().into_iter().zip(old.iter().map(|(key, value)| some_conversion(value)).collect();
The issue is that iter()
(docs) returns a 'non-consuming' iterator which hands out references to the underlying values ([1]). The new HashMap
cannot be constructed using references (&String
), it needs values (String
).
In your example, some_conversion
seems to return a new String
for the value part, so applying .clone()
to the key would do the trick:
let old: HashMap<String, Value> = some_origin();
let new: HashMap<String, String> = old.iter().map(|(key, value)| {
return (key.clone(), some_conversion(value));
// ^---- .clone() call inserted
}).collect();
Here is a link to a full example on the rust playground.
Looking at the error message from the compiler [2], this is indeed quite hard to figure out. I think what most helps with this is to build up an intuition around references and ownership in Rust to understand when references are OK and when an owned value is needed.
While I'd recommend reading the sections on references and ownership in the Rust Book and even more Programming Rust, the gist is as follows:
Rc
).Copy
" types which are cheap to copy, such as i32
).How does this help?
I hope this gives some intuition (again I would really recommend Programming Rust on this). In general, if you do something with a value, you either take ownership over it, or you get a reference. If you take ownership, the original variable that had ownership can no longer be used. If you get a reference, you can't hand ownership to somebody else (without cloning). And Rust doesn't clone for you.
[1]: The docs call this "An iterator visiting all key-value pairs in arbitrary order. The iterator element type is (&'a K, &'a V)." Ignoring the 'a
lifetime parameters, you can see that the element type is (&K, &V)
.
[2]:
13 | .collect();
| ^^^^^^^ value of type `std::collections::HashMap<std::string::String, std::string::String>` cannot be built from `std::iter::Iterator<Item=(&std::string::String, std::string::String)>`
|
= help: the trait `std::iter::FromIterator<(&std::string::String, std::string::String)>` is not implemented for `std::collections::HashMap<std::string::String, std::string::String>`
If you don't need the old map any more you can just use into_iter
.
let new: HashMap<String, String> = old.into_iter().map(|(key, value)| {
return (key, some_conversion(value));
}).collect();
You can see a working version here
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With