I am trying to figure out why this does not work (Playground):
fn main() {
let a = vec![1, 2, 3, 4];
let b = a.clone();
// slice and iter (wrong way)
let s: i32 = &a[1..a.len()].iter()
.zip(&b[1..b.len()].iter())
.map(|(x, y)| x * y)
.sum();
println!("{}", s);
}
Error:
rustc 1.13.0 (2c6933acc 2016-11-07)
error[E0277]: the trait bound `&std::slice::Iter<'_, {integer}>: std::iter::Iterator` is not satisfied
--> <anon>:6:10
|
6 | .zip(&b[1..b.len()].iter())
| ^^^ trait `&std::slice::Iter<'_, {integer}>: std::iter::Iterator` not satisfied
|
= note: `&std::slice::Iter<'_, {integer}>` is not an iterator; maybe try calling `.iter()` or a similar method
= note: required because of the requirements on the impl of `std::iter::IntoIterator` for `&std::slice::Iter<'_, {integer}>`
error: no method named `map` found for type `std::iter::Zip<std::slice::Iter<'_, {integer}>, &std::slice::Iter<'_, {integer}>>` in the current scope
--> <anon>:7:10
|
7 | .map(|(x, y)| x * y)
| ^^^
|
= note: the method `map` exists but the following trait bounds were not satisfied: `&std::slice::Iter<'_, {integer}> : std::iter::Iterator`, `std::iter::Zip<std::slice::Iter<'_, {integer}>, &std::slice::Iter<'_, {integer}>> : std::iter::Iterator`
But this does work:
fn main() {
let a = vec![1, 2, 3, 4];
let b = a.clone();
// slice and iter (correct way)
let s: i32 = a[1..a.len()].iter()
.zip(b[1..b.len()].iter())
.map(|(x, y)| x * y)
.sum();
println!("{}", s);
}
Please explain how vectors work in Rust and the difference above when I iter()
.
In short: you probably misunderstood operator precedence:
&b[1..b.len()].iter()
Is equal to:
&(b[1..b.len()].iter())
And since zip()
is expecting something that implements IntoIterator
, the call fails, since a reference to this iterator type does not implement said trait.
Let's try to understand the error message! Of course, we will first just look at the first error:
error[E0277]: the trait bound `&std::slice::Iter<'_, {integer}>: std::iter::Iterator` is not satisfied
--> <anon>:6:10
|
6 | .zip(&b[1..b.len()].iter())
| ^^^ trait `&std::slice::Iter<'_, {integer}>: std::iter::Iterator` not satisfied
|
= note: `&std::slice::Iter<'_, {integer}>` is not an iterator; maybe try calling `.iter()` or a similar method
= note: required because of the requirements on the impl of `std::iter::IntoIterator` for `&std::slice::Iter<'_, {integer}>`
Wow, that's quite a mouthful. But we can see that some trait bound requirement of the function zip()
is violated. So, let's look at the signature of said function:
fn zip<U>(self, other: U) -> Zip<Self, U::IntoIter>
where U: IntoIterator
What matters is the other
argument (type U
). U
has to be IntoIterator
. This trait is implemented for quite a few types ... let's check what type we're trying to pass into zip()
:
&b[1..b.len()].iter()
To analyze this completely, we need to understand quite something, but I'll try to break it down. First, let's disambiguate operator precedence by inserting more parenthesis. The above code snippet is equivalent to:
&(b[1..b.len()].iter())
An expression foo[bar]
desugares to *::std::ops::Index::index(&foo, bar)
. This is the most complex part here, but looking this up in the documentation reveals that the expression b[1..b.len()]
has the type [i32]
.
On that type, you call iter()
which returns a type Iter<_, _>
which is the iterator type for slices.
Now the&
: you borrow this Iter<_, _>
thing, resulting in &Iter<_, _>
.
And hey, this matches the error message! Look at the last note:
note: required because of the requirements on the impl of `std::iter::IntoIterator` for `&std::slice::Iter<'_, {integer}>`
So... what does satisfy the IntoIterator
trait? For one, every type that implements Iterator
(e.g. Iter<_, _>
) also implements IntoIterator
. So you can just remove the &
in the expression and it works!
But we can do even better! IntoIterator
is also implemented for &[T]
, so you can also just remove the .iter()
and it works!
let s: i32 = a[1..].iter()
.zip(&b[1..])
.map(|(x, y)| x * y)
.sum();
Note: I also removed the ranges' upper bounds to make them half open, as Paolo Falabella suggested.
Your first version has an issue with operator precedence: &a[1..a.len()].iter()
applies iter()
first and then takes a reference to it, ending with a reference to a std::slice::Iter
.
As you can see on the docs for Iter
, there is an impl Iterator
for Iter
but not for &Iter
.
This is what the first error is trying to say: (look at the part that says: &std::slice::Iter<'_, {integer}>
is not an iterator).
Simplifying a bit, you can have:
fn main() {
let a = vec![1, 2, 3, 4];
// let b = a.clone(); // no more need to clone. We're going to only
// work with references
let s: i32 = (&a[1..]).iter() // you don't need the a.len()
// to slice to the end
.zip(&a[1..]) // &a implements IntoIter, which zip
// accepts, so you don't need iter()
.map(|(x, y)| x * y)
.sum();
println!("{}", s);
}
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