1

Let's say I want to double each value in an iterator of numbers. I could do this:

vec![1, 2, 3]
    .into_iter()
    .map(|x| x * 2)
    .for_each(|x| println!("{}", x)); //Prints 2, 4, 6.

To get cleaner code, I would prefer to do this:

vec![1, 2, 3]
    .into_iter()
    .double() //I need to implement this.
    .for_each(|x| println!("{}", x));

How do I write my own chainable iterator function, like double in this example? I guess I will have to create an interface and implement it for Iterator? There are a lot of types to get right, so a working solution for this silly example would be helpful.

Anders
  • 8,307
  • 9
  • 56
  • 88
  • The best way to learn this is to look in the source for any of the built-in iterator functions. For example [enumerate](https://doc.rust-lang.org/src/core/iter/iterator.rs.html#783-785) is a simple one. The implementation of the struct is [here](https://doc.rust-lang.org/src/core/iter/mod.rs.html#1661-1688). Note that this implementation overrides a lot of methods, but you only need to define `next`. – Peter Hall Jan 27 '19 at 16:45
  • This is a good thing to learn, but I would argue that this is not "cleaner" in this particular case. It will introduce a lot of code which is not really necessary compared to just using `map` as intended. – Peter Hall Jan 27 '19 at 16:46
  • @E4_net_or_something_like_that Thanks for the links. I agree that it is not cleaner in this simple case, but I needed a simple example so as not to make the question to broad. – Anders Jan 27 '19 at 16:50

1 Answers1

2

First you have to create a new-type to implement your iterator. The simplest way is to wrap a iterator of the type you want to target:

struct Double<T: ?Sized> {
    inner: T,
}

Now you just implement the Iterator trait for this type if the innert type T is of the correct type. Something like:

impl<T> Iterator for Double<T>
where T: Iterator<Item = i32> {
    type Item = i32;
    fn next(&mut self) -> Option<i32> {
        self.inner.next().map(|x| 2*x)
    }
}

And that's it! You just need to add a Double constructor. The most ergonomic solution is to extend Iterator. Something like this:

trait Doubler : Iterator<Item = i32> {
    fn into_double(self) -> Double<Self>;
}

impl<T> Doubler for T
where T: Iterator<Item = i32> {
    fn into_double(self) -> Double<Self> {
        Double { inner: self }
    }
}

An example of usage (playground):

fn main() {
    vec![1, 2, 3]
        .into_iter()
        .into_double()
        .for_each(|x| println!("{}", x));
}
rodrigo
  • 94,151
  • 12
  • 143
  • 190