Reputation: 32071
An algorithm I wrote builds a temporary HashMap
. Once it's finished, I'm only interested in the values
of the hashmap, so I'd like to transfer ownership of the values from the HashMap<K, V>
to a Vec<V>
.
With a simplified example hashmap:
fn main() {
use std::collections::HashMap;
let mut h: HashMap<_, _> = HashMap::new();
h.insert(1, "foo".to_owned());
}
I can do:
let vals: Vec<&String> = h.values().collect();
- which is short and sweet, but the hashmap still owns the values;let vals: Vec<String> = h.values().cloned().collect()
(as in this question) - the result is what I need, but I was taught to avoid the extra clones;let vals: Vec<String> = h.into_iter().map(|(_k, v)| v).collect();
- does what I need without a clone, but is a bit ugly.The actual values are a mid-sized struct ({String, Vec<String>}
, under a KB total).
Should I default to avoiding clone
in this case or is it premature optimization? Is there an idiomatic way to do this that I'm missing?
Upvotes: 1
Views: 1630
Reputation: 2490
Note that as of Rust 1.54.0, the into_values
method has been implemented for HashMap
and BTreeMap
in the standard library.
fn main() {
let mut h: HashMap<i32, String> = HashMap::new();
h.insert(1, "foo".to_owned());
let _vals: Vec<String> = h.into_values().collect();
}
https://github.com/rust-lang/rust/blob/master/RELEASES.md#stabilized-apis-6
Upvotes: 2
Reputation: 26765
.into_iter().map(|(_, v)| v)
is the idiomatic way to do it. That not ugly at all.
If you want you could do:
use std::collections::hash_map;
use std::collections::HashMap;
use std::iter::{ExactSizeIterator, FusedIterator};
struct IntoValues<K, V> {
iter: hash_map::IntoIter<K, V>,
}
impl<K, V> IntoValues<K, V> {
fn new(map: HashMap<K, V>) -> Self {
Self {
iter: map.into_iter(),
}
}
}
impl<K, V> Iterator for IntoValues<K, V> {
type Item = V;
fn next(&mut self) -> Option<Self::Item> {
self.iter.next().map(|(_, v)| v)
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.iter.size_hint()
}
}
impl<K, V> ExactSizeIterator for IntoValues<K, V> {}
impl<K, V> FusedIterator for IntoValues<K, V> {}
trait HashMapTool {
type IntoValues;
type Item;
fn into_values(self) -> Self::IntoValues;
}
impl<K, V> HashMapTool for HashMap<K, V> {
type Item = V;
type IntoValues = IntoValues<K, V>;
fn into_values(self) -> Self::IntoValues {
IntoValues::new(self)
}
}
fn main() {
let mut h: HashMap<_, _> = HashMap::new();
h.insert(1, "foo".to_owned());
let _vals: Vec<_> = h.into_values().collect();
}
Upvotes: 3