Introduction
In Design Patterns, the Iterator is a way of traversing over a container, that is access each of the container’s elements. It is used as a convenient way to traverse over lists or arrays, but it could also be used to traverse over binary trees for example.
What does it look like. It looks like this:
A bit of explanation can be handy:
- An iterator basically has two methods: next() and hasNext()
- The next() method returns the next element in the container
- The hasNext() method returns a bool, true if a next element is available, false if there is no next element
Implementation in Go
To demonstrate this pattern I will build an iterator that will just yield even numbers up to a certain user-defined limit.
In an empty directory type:
go mod init github.com/go-iterator-pattern
Now open your favourite IDE and add a ‘main.go’ file. We will start with the preliminaries:
package main
Next we define the EvenNumberIterator:
type EvenNumberIterator struct {
current int32
limit int32
}
A short explanation:
- current is used to keep track of which number we are delivering
- limit is user-defined and makes sure we only yield a certain number of elements
Next we define the constructor for this struct:
func NewEvenNumberIterator(limit int32) *EvenNumberIterator {
return &EvenNumberIterator{current: 0, limit: limit}
}
Some points:
- We return a reference to the iterator. Considering that the size of this struct is small, this might seem superfluous, however, with bigger structures this can be a time- and memorysaver.
- The iterator is initialized with the limit set by the user, and current set to 0, as we will start from zero for this example.
Now we will define the two basic iterator methods:
func (i *EvenNumberIterator) Next() int32 {
i.current++
if i.current%2 >= i.limit {
return 0
}
return i.current * 2
}
func (i *EvenNumberIterator) HasNext() bool {
return i.current < i.limit
}
Again, a short breakdown:
- The Next() method returns the next number if there is one. It checks if the current value is not over the limit, and since we only yield even numbers, we check if current%2 i.e. current modulo 2 is not over the limit
- The HasNext() method returns true if we have more numbers, false if there are none.
Time to test
To test this, we will add a main function:
func main() {
iterator := NewEvenNumberIterator(10)
for iterator.HasNext() {
println(iterator.Next())
}
}
All this does is:
- Instantiate the iterator
- Loop over it using the for statement, retrieving the next element using the Next() method, and making sure we are in the bounds of our limit with the HasNext() method
Conclusion
As you can see, as with many things in Go, it is very easy to build a simple iterator. My advice however would be that whenever you can, use Go’s built-in iterators.
One possible extension of this iterator would be to make it multi-threaded, but that will be the subject of another post.