# iterators

> The iterator pattern allows you to perform some task on a sequence of items
> in turn. An iterator is responsible for the logic of iterating over each item
> and determining when the sequence has finished.
[Rust book](https://doc.rust-lang.org/book/ch13-02-iterators.html)

An itetarator it's usually related to some sort of lists, vectors or collection
of items, but the iterator can be anything that implements the interface. The
interaface depends on the language, but usually only needs a `next(Self) -> Item`
method that returns the next item to be acted upon.

The adventage of iterators is that you can use them with anything that accept
thems (kinda in the reusable side of programming), and modern standard
libraries implements patterns to use them like: `forEach`, `map`, `sum`, etc

Another common use case is to iterate on something indefinetly as long as it
continue to provide new items. For example you can implement a
`PollingIterator` that fetch data from a remote source until the source says
there's no more data:

```python
class EmailPoll:
    """Iterator class that pools emails from the EmailService indefinetly."""

    def __init__(self, service: EmailService, wait_time: int):
        super(EmailPoll, self).__init__()
        self.service = service
        self.wait_time = wait_time
        self.current_mails = self.service.fetch_emails()

    def __iter__(self):
        return self

    def __next__(self):
        while len(self.current_mails) == 0:
            self.current_mails = self.service.fetch_emails()

            if len(self.current_mails) == 0:
                print(f"No new emails, waiting {self.wait_time}s")
                sleep(self.wait_time)

        return self.current_mails.pop(0)


email_service = EmailService()
for mail in EmailPoll(email_service, 3):
    print(mail)

```
