Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Slice and iter() simultaneously

Tags:

rust

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().

like image 854
Abhijay Ghildyal Avatar asked Dec 09 '16 13:12

Abhijay Ghildyal


2 Answers

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.


Full Explanation

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())
  1. 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].

  2. On that type, you call iter() which returns a type Iter<_, _> which is the iterator type for slices.

  3. 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!

Working Code

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.

like image 165
Lukas Kalbertodt Avatar answered Nov 09 '22 01:11

Lukas Kalbertodt


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);
}
like image 34
Paolo Falabella Avatar answered Nov 09 '22 02:11

Paolo Falabella