Reputation: 41
Why does this code work properly
fn main() {
let v1 = vec!["lemonade", "lemon", "type", "lid"];
println!("{:?}", v1.iter().filter(|item| item.starts_with("l")).collect::<Vec<_>>());
}
While this code gets me an error, I kinda get the sense why this isn't working and I get how to fix it, but I don't really understand what type is it returning so I can replace the "_" to something not that generic
fn main() {
let v1 = vec!["lemonade", "lemon", "type", "lid"];
println!("{:?}", v1.iter().filter(|item| item.starts_with("l")).collect::<Vec<&str>>());
}
The error
error[E0277]: a value of type `Vec<&str>` cannot be built from an iterator over elements of
type `&&str`
--> src\main.rs:3:69
|
3 | println!("{:?}", v1.iter().filter(|item| item.starts_with("l")).collect::
<Vec<&str>>());
| ^^^^^^^ value of type
`Vec<&str>` cannot be built from `std::iter::Iterator<Item=&&str>`
|
= help: the trait `FromIterator<&&str>` is not implemented for `Vec<&str>`
= help: the trait `FromIterator<T>` is implemented for `Vec<T>`
note: required by a bound in `collect`
--> C:\Users\Mululi\.rustup\toolchains\stable-x86_64-pc-windows-
msvc\lib/rustlib/src/rust\library\core\src\iter\traits\iterator.rs:1788:19
|
1788 | fn collect<B: FromIterator<Self::Item>>(self) -> B
| ^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `collect`
I couldn't fully understand what the compiler meant by that
Upvotes: 4
Views: 1320
Reputation: 361585
Let's look at the types in detail. It will take a couple of documentation derefs to get to the bottom so bear with me.
let v1 = vec!["lemonade", "lemon", "type", "lid"];
v1
is a Vec<&str>
.
v1.iter()
Vec
doesn't have an iter
method, but it implements Deref<Target = [T]>
which does:
pub fn iter(&self) -> Iter<'_, T>
Iter
here is a struct named std::slice::Iter
. What kind of iterator is it? We need to take a look at its Iterator
implementation:
impl<'a, T> Iterator for Iter<'a, T> {
type Item = &'a T;
}
This tells us that the iterator yields item of type &T
. Remember that we have a Vec<&str>
, so T = &str
. That means that what we really have is an Iterator<Item = &&str>
. That's the source of the error you're getting.
So, how do we get an Iterator<Item = &str>
instead of an Iterator<Item = &&str>
? There are several ways, including:
into_iterator()
Use Vec
's implementation of IntoIterator
which has Item = T
. into_iterator()
yields T
s instead of &T
s.
v1.into_iter().filter(...)
This is efficient, but note that it consumes the vector. It doesn't return an iterator that references the items, it actually moves the items out of the vector and into the consuming code. The vector is unuseable after calling into_iter
.
iter().copied()
If T: Copy
you can call copied
to turn an Iterator<Item = &T>
into an Iterator<Item = T>
.
v1.iter().copied().filter(...)
This technique is great because it doesn't consume the vector, and it will work because &str
is indeed Copy
. All references are Copy
thanks to this blanket implementation:
impl<T: ?Sized> Copy for &T {}
iter().cloned()
If T: Clone
you can call cloned
to clone all the items and turn an Iterator<Item = &T>
into an Iterator<Item = T>
.
v1.iter().cloned().filter(...)
Anything that's Copy
is also Clone
, so you could indeed clone the &str
references.
Upvotes: 7
Reputation: 70900
You need to collect into Vec<&&str>
, not Vec<&str>
. The original vector is Vec<&str>
, and iter()
produces an iterator over references to the elements, so the iterator yields &&str
s.
Upvotes: 0