Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to Iterator::chain a vector of iterators?

Tags:

rust

for a given set of iterators a, b, c, one can chain them successfully with a.chain(b).chain(c). Since the CLI util I am trying to write provides a vector of paths (strings, --dirs "a/b/c" "d/e/f" ...), I would like to use walkd_dir on each of them and then chain them together. My first thought is:

fn main() {
    let a = 0..3;
    let b = 3..6;
    let c = 6..9;
    let v = vec![b, c];
    v.iter().cloned().fold(a, |acc, e| acc.chain(e));
}

http://is.gd/hfNQd2, returns

<anon>:6:40: 6:52 error: mismatched types:
 expected `core::ops::Range<_>`,
    found `core::iter::Chain<core::ops::Range<_>, core::ops::Range<_>>`
(expected struct `core::ops::Range`,
    found struct `core::iter::Chain`) [E0308]
<anon>:6     v.iter().cloned().fold(a, |acc, e| acc.chain(e));

Another attempt http://is.gd/ZKdxZM, although a.chain(b).chain(c) works.

like image 584
ifett Avatar asked Sep 26 '15 13:09

ifett


2 Answers

Use flat_map:

fn main() {
    let a = 0..3;
    let b = 3..6;
    let c = 6..9;
    let v = vec![a, b, c];
    v.iter().flat_map(|it| it.clone());
}
like image 155
bluss Avatar answered Oct 04 '22 12:10

bluss


As the error message states, the type of a Range is different than the type of Chain<Range, Range>, and the type of the accumulator in the call the fold must always be consistent. Otherwise, what would the return type be from the fold if there were no items in the vector?

The simplest solution is to use a trait object, specifically Box<Iterator>:

type MyIter = Box<Iterator<Item=i32>>;

fn main() {
    let a = 0..3;
    let b = 3..6;
    let c = 6..9;
    let v = vec![b, c];
    let z = v.into_iter().fold(Box::new(a) as MyIter, |acc, e| {
        Box::new(acc.chain(Box::new(e) as MyIter)) as MyIter
    });

    for i in z {
        println!("{}", i);
    }
}

This adds a level of indirection but unifies the two concrete types (Range, Chain) as a single type.

A potentially more efficient but longer-to-type version would be to create an enum that represented either a Range or a Chain, and then implement Iterator for that new type.

Actually, I don't think the enum would work as it would require a recursive definition, which is not allowed.

like image 22
Shepmaster Avatar answered Oct 04 '22 13:10

Shepmaster