Peter Hall
Peter Hall

Reputation: 58825

Chaining iterators of different types

I get type errors when chaining different types of Iterator.

let s = Some(10);
let v = (1..5).chain(s.iter())
        .collect::<Vec<_>>();

Output:

<anon>:23:20: 23:35 error: type mismatch resolving `<core::option::Iter<'_, _> as core::iter::IntoIterator>::Item == _`:
 expected &-ptr,
    found integral variable [E0271]
<anon>:23     let v = (1..5).chain(s.iter())
                             ^~~~~~~~~~~~~~~
<anon>:23:20: 23:35 help: see the detailed explanation for E0271
<anon>:24:14: 24:33 error: no method named `collect` found for type `core::iter::Chain<core::ops::Range<_>, core::option::Iter<'_, _>>` in the current scope
<anon>:24             .collect::<Vec<_>>();
                       ^~~~~~~~~~~~~~~~~~~
<anon>:24:14: 24:33 note: the method `collect` exists but the following trait bounds were not satisfied: `core::iter::Chain<core::ops::Range<_>, core::option::Iter<'_, _>> : core::iter::Iterator`
error: aborting due to 2 previous errors

But it works fine when zipping:

let s = Some(10);
let v = (1..5).zip(s.iter())
        .collect::<Vec<_>>();

Output:

[(1, 10)]

Why is Rust able to infer the correct types for zip but not for chain and how can I fix it? n.b. I want to be able to do this for any iterator, so I don't want a solution that just works for Range and Option.

Upvotes: 2

Views: 2859

Answers (1)

Shepmaster
Shepmaster

Reputation: 431809

First, note that the iterators yield different types. I've added an explicit u8 to the numbers to make the types more obvious:

fn main() {
    let s = Some(10u8);
    let r = (1..5u8);

    let () = s.iter().next(); // Option<&u8>
    let () = r.next();        // Option<u8>
}

When you chain two iterators, both iterators must yield the same type. This makes sense as the iterator cannot "switch" what type it outputs when it gets to the end of one and begins on the second:

fn chain<U>(self, other: U) -> Chain<Self, U::IntoIter> 
    where U: IntoIterator<Item=Self::Item>
//                        ^~~~~~~~~~~~~~~ This means the types must match

So why does zip work? Because it doesn't have that restriction:

fn zip<U>(self, other: U) -> Zip<Self, U::IntoIter> 
    where U: IntoIterator
//                       ^~~~ Nothing here!

This is because zip returns a tuple with one value from each iterator; a new type, distinct from either source iterator's type. One iterator could be an integral type and the other could return your own custom type for all zip cares.

Why is Rust able to infer the correct types for zip but not for chain

There is no type inference happening here; that's a different thing. This is just plain-old type mismatching.

and how can I fix it?

In this case, your inner iterator yields a reference to an integer, a Clone-able type, so you can use cloned to make a new iterator that clones each value and then both iterators would have the same type:

fn main() {
    let s = Some(10);
    let v: Vec<_> = (1..5).chain(s.iter().cloned()).collect();
}

If you are done with the option, you can also use a consuming iterator with into_iter:

fn main() {
    let s = Some(10);
    let v: Vec<_> = (1..5).chain(s.into_iter()).collect();
}

Upvotes: 10

Related Questions