How to write iterator adapter


#1

I’ve spent several evenings on this problem and I can not solve it. I have an array of f64 complex numbers in flat array (Re, Im) of type &[f64]. I want to convert it to &[Complex64] format and than apply some complex number function, for example norm(). I have implemented my own structure to keep the state and Iterator trait for it, but I can not figure out the final step, how do I connect it to the actual code so I can call something like (1…10).tuples()

struct Tuples<I> {
    orig: I
}

impl<I> Iterator for Tuples<I> where I: Iterator {
    type Item = (I::Item, I::Item);

    #[inline]
    fn next(&mut self) -> Option<Self::Item> {
        if let Some(t1) = self.orig.next() {
            if let Some(t2) = self.orig.next() {
                return Some((t1, t2));
            }
        }
        return None;
    }

    #[inline]
    fn size_hint(&self) -> (usize, Option<usize>) {
        match self.orig.size_hint() {
            (lower, Some(upper)) => (lower, Some(upper / 2)),
            h @(_, _) => h
        }
    }
}



#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn should_skip_last() {
        let a = &[1,2,3,4,5];
        let it = a.iter().tuples();
        assert!((1,2), it.next());
        assert!((3,4), it.next());
        assert!(None, it.next());
    }
}

#2

Hi!

The most simple thing is to declares a function, so that you can call it like tuples(xs.iter()). If you want to have a method call syntax, you’ll need to create your own trait and make sure that every iterator implements the said trait.

pub struct Tuples<I> {
    orig: I
}

impl<I> Iterator for Tuples<I> where I: Iterator {
    type Item = (I::Item, I::Item);

    #[inline]
    fn next(&mut self) -> Option<Self::Item> {
        if let Some(t1) = self.orig.next() {
            if let Some(t2) = self.orig.next() {
                return Some((t1, t2));
            }
        }
        return None;
    }

    #[inline]
    fn size_hint(&self) -> (usize, Option<usize>) {
        match self.orig.size_hint() {
            (lower, Some(upper)) => (lower, Some(upper / 2)),
            h @ (_, _) => h
        }
    }
}

pub fn tuples<I: Iterator>(xs: I) -> Tuples<I> {
    Tuples { orig: xs }
}

pub trait MyIterExt: Sized {
    fn tuples(self) -> Tuples<Self>;
}

impl <I: Iterator> MyIterExt for I {
    fn tuples(self) -> Tuples<Self> {
        tuples(self)
    }
}


#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn should_skip_last_fn() {
        let a = &[1, 2, 3, 4, 5];
        let mut it = tuples(a.iter().cloned());
        assert_eq!(Some((1, 2)), it.next());
        assert_eq!(Some((3, 4)), it.next());
        assert_eq!(None, it.next());
    }

    #[test]
    fn should_skip_last_method() {
        let a = &[1, 2, 3, 4, 5];
        let mut it = a.iter().cloned().tuples();
        assert_eq!(Some((1, 2)), it.next());
        assert_eq!(Some((3, 4)), it.next());
        assert_eq!(None, it.next());
    }
}

#3

Very cool! I always thought it was necessary to parameterize Iterator like Iterator<Item=K> where
K would be the original returned value, but here it is and it works…


#4

Thank you @matklad, it works. But at the same time, I have to admit that while I can interpret this code, I am at the same point where I was before: I would not be able to solve this problem should I face it again. Any advice where I can learn how to use rust generics? Documentation explain the basics but does not teach how to solve even basic tasks, because it is so radical departure from OOP, I can’t rewire my brain using those basic examples in the doc.

Just for example, I do not understand why this does not work:
impl TuplesImpl for Iterator {
and this does:
impl <I: Iterator> TuplesImpl for I {

To my eye those 2 constructs declare the same intention.


#5

Any advice where I can learn how to use rust generics? Documentation explain the basics but does not teach how to solve even basic tasks, because it is so radical departure from OOP, I can’t rewire my brain using those basic examples in the doc.

Yeah, the docs are quite light on the topic of actually using generics. Have you seen the chapter in the new book http://rust-lang.github.io/book/ch10-00-generics.html? It is not complete yet, but it may contain some useful examples! What works for me to learn more advanced features of Rust is asking questions at users.rust-lang.org :slight_smile:

If you know something about C++ templates or Haskell typeclasses it may be useful to contrast it with Rust traits. There are no close analogs in the OOP world. Also If you happen to know Russian you might find this fragment of my talk helpful: https://www.youtube.com/watch?v=TiCSWNpeR9Q&feature=youtu.be&t=48m25. I don’t “explain” generics there, but I try to contrast them with OOP features.

To my eye those 2 constructs declare the same intention.

Haha, this is the exact same problem I once faced myself: Confusion about impls without for :slight_smile: There’s a surprising syntactic “overload” for the traits.

If we have a trait Foo, then we can use it as a bound for some type parameter T. That is, you use T as a type name, and write T: Foo in < > or the where clause. This can be read as “for all types T that implement Foo, …”.

However, and here’s the catch, you can also use Foo as a type name! And in the type position, the name of the trait means a specific concrete type: trait object: https://doc.rust-lang.org/beta/book/trait-objects.html.

So your second example implements TuplesImpl for a potentially unbounded number of types, and your first example implements TuplesImpl only for Iterator trait objects.