Getting confused trying to implement Iterator

The code below works, but I want to be able to use self.iter() as in the comment in roll3(). I tried reading other posts but got pretty confused about the right way to do it. For example, do I need to implement Into_Iterator? Thanks! -Joe

struct RerollableDie {
    sides: u16,
    roll_count: u16,
    curval: u16,
}

impl RerollableDie {
    fn new(sides: u16) -> Self { 
        Self { 
            sides,
            roll_count: 0,
            curval: 0,
        } 
    }

    fn roll3(&mut self) -> u16 {
        // I want to be able to do this instead: self.iter().take(3).sum()
        let v1 = self.next().unwrap();
        let v2 = self.next().unwrap();
        let v3 = self.next().unwrap();

        v1 + v2 + v3

    }
}

impl Iterator for RerollableDie {
    type Item = u16;
    fn next(&mut self) -> Option<Self::Item> {
        self.curval += 1;
        let retval = self.curval;
        self.curval %= self.sides;
        self.roll_count += 1;
        Some(retval)
    }
}

RerollableDie is an iterator so you can just do self.take(3).sum()

2 Likes

To clarify a bit more, iter isn't a trait method -- it's just a conventional name for collections like Vec. On those collections, it is usually equivalent to having something like all of this:

struct BorrowedIter<'a, T> { /* ... */ }
impl<'a, T> Iterator for BorrowedIter<'a, T> {
    type Item = &'a T;
    /* ... */
}
impl<T> Collection<T> {
    fn iter(&self) -> BorrowedIter<'_, T> {
        /* ... */
    }
}
impl<'a, T> IntoIterator for &'a Collection<T> {
    type Item = <Self::IntoIter as Iterator>::Item;
    type IntoIter = BorrowedIter<'a>;
    fn into_iter(self) -> Self::IntoIter {
        self.iter() // Collection::<T>::iter(self)
    }
}

But since your struct is an iterator, you don't need any of that; just use your self.


If you're used to for where you have to do something like...

// Works but then you realize you can't let it consume your vector
for value in my_vec { /* ... */ }
// Works but then you realize you need just the first three
for value in &my_vec { /* ... */ }
// Gives you some error about `Vec` not being an iterator
for value in my_vec.take(3) { /* ... */ }
// Works again
for value in my_vec.iter().take(3) { /* ... */ }

...and that is throwing you off, it may help to know that for uses IntoIterator, not Iterator. That's why &my_vec works, for example. But my_vec.take(3) doesn't work because take is an iterator method -- you have to create the iterator first before calling it -- and collections like Vec are not themselves iterators. When you use a Vec with for like in the first line, it uses my_vec.into_iter() to transform the Vec into some kind of owning iterator -- a different type.

That's also why Vec and other collections have the iter method, to easily create a borrowing iterator. It's a lot nicer than writing this:

for value in (&my_vec).into_iter().take(3) { /* ... */ }

Every iterator also implements IntoIterator -- the implementation simply returns the same object. But again, your struct is an iterator already, so you don't need to use .into_iter() either. That is, if you were working with a for loop, this would work (unlike the example above):

for value in self.take(3) { /* ... */ }
3 Likes