Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Type issue with Iterator collect

Tags:

iterator

rust

I am trying to convert a vector of &str pairs into a HashMap with the following code snippet:

use std::collections::HashMap;  fn main() {   let pairs = vec!(("foo", "bar"), ("toto", "tata"));   let map: HashMap<&str, &str> = pairs.iter().collect();   println!("{:?}", map); } 

However the compilation fails with this error:

<anon>:5:47: 5:56 error: the trait `core::iter::FromIterator<&(&str, &str)>` is not implemented for the type `std::collections::hash::map::HashMap<&str, &str>` [E0277] <anon>:5   let map: HashMap<&str, &str> = pairs.iter().collect(); 

However if I add .cloned() before calling collect() everything works fine:

... let map: HashMap<&str, &str> = pairs.iter().cloned().collect(); ... 

Even if I understand the error message (there is no implementation of the trait FromIterator<&(&str, &str)> for the type HashMap<&str, &str>) I do not understand where the type &(&str, &str) comes from (according to the method signature in the Rust documentation) and why calling cloned() fixes that problem.

like image 590
Jcs Avatar asked Sep 02 '15 13:09

Jcs


Video Answer


1 Answers

The type &(&str, &str) comes from what iter() on a Vec returns:

fn iter(&self) -> Iter<T> 

where Iter<T> implements Iterator<Item=&T>:

impl<'a, T> Iterator for Iter<'a, T> {     type Item = &'a T     ... } 

In other words, iter() on a vector returns an iterator yielding references into the vector.

cloned() solves the problem because it is an iterator adapter which converts Iterator<Item=&T> to Iterator<Item=T> if T is cloneable. You can think of it as a shorthand for map(|v| v.clone()):

let v1: Vec<i32> = vec![1, 2, 3, 4]; let v2: Vec<_> = v1.iter().cloned().collect(); let v3: Vec<_> = v1.iter().map(|v| v.clone()).collect(); assert_eq!(v2, v3); 

It happens that (&str, &str) is cloneable because each tuple component is also cloneable (all references are), so cloned() would return an object which implements Iterator<Item=(&str, &str)> - exactly what collect() needs to create a HashMap.

Alternatively, you can use into_iter() to get Iterator<Item=T> from Vec<T>, but then the original vector will be consumed:

use std::collections::HashMap;  fn main() {     let pairs = vec!(("foo", "bar"), ("toto", "tata"));     let map: HashMap<&str, &str> = pairs.into_iter().collect();     println!("{:?}", map); } 
like image 83
Vladimir Matveev Avatar answered Sep 20 '22 04:09

Vladimir Matveev