kenta_desu
kenta_desu

Reputation: 383

Implementing a custom Iterator Trait

Source code

pub struct Iterating_ex {
    start: u32,
    end: u32,
}

impl Iterator for Iterating_ex {
    type Item = u32;

    fn next(&mut self) -> Option<u32> {
        if self.start >= self.end {
            None
        } else {
            let result = Some(self.start);
            self.start += 1;
            result
        }
    }
}

fn main() {
    let example = Iterating_ex {
        start: 0,
        end: 5,
    };
    for i in example {
        println!("{i}");
    }
}

Output

0
1
2
3
4

Individually I understand what each piece of code is trying to do, however I am having trouble understanding the following, possibly due to my lack of understanding of the generic iterator trait;

  1. Does implementing the Iterator trait for a struct automatically generate an iterable data type? In this case, I don't know why a for loop can be used on example.
  2. It seems like the next method is called as a loop until None is returned. How does the code know to do so?

Upvotes: 2

Views: 879

Answers (1)

Aleksander Krauze
Aleksander Krauze

Reputation: 6255

Ad 1. To be "iterable" in rust means to implement the Iterator trait. Some things however can be turned into an iterator and that is described by another trait IntoIterator. Standard library provides a blanket implementation:

impl<I: Iterator> IntoIterator for I { /* ... */}

Which means that any type that implements Iterator can be turned into one (it's noop). for loops are designed to work with types that implement IntoIterator. That's why you can write for example:

let mut v = vec![1, 2, 3, 4, 5];

for _ in &v {}
for _ in &mut v {}
for _ in v {}

Since types &Vec<T>, &mut Vec<T> and Vec<T> all implement IntoIterator trait. They all are turned into different iterator types of course, and are returning respectively &T, &mut T and T.

Ad 2. As stated before for loops can be used on types that implement IntoIterator. The documentation explains in detail how it works, but in a nutshell for loop is just a syntax sugar that turns this code:

for x in xs {
    todo!()
}

Into something like this:

let mut xs_iter = xs.into_iter();
while let Some(x) = xs_iter.next() {
    todo!()
}

while loops are also syntax sugar and are de-sugared into loop with a break statement but that's not relevant here.

Side note. I guess that this is just a learning example, and it's great, but the exact same iterator already exists in the standard library as std::ops::Range, so use that it your actual code if you need it.

Upvotes: 6

Related Questions