Range Over Iterators in Swift

Starting with version 1.23, Go has added support for iterators, which lets us range over pretty much anything!

Let’s look at the List type from the previous example again. In that example we had an AllElements method that returned a slice of all elements in the list. With Go iterators, we can do it better - as shown below.

struct List<Element> {
    var head: ElementNode<Element>?
    var tail: ElementNode<Element>?

    mutating func push(_ value: Element) {
        let newNode = ElementNode(value: value)
        if tail == nil {
            head = newNode
            tail = newNode
        } else {
            tail?.next = newNode
            tail = newNode
        }
    }

    func all() -> AnyIterator<Element> {
        var current = head
        return AnyIterator {
            if let nextNode = current {
                current = nextNode.next
                return nextNode.value
            } else {
                return nil
            }
        }
    }
}

class ElementNode<Element> {
    var value: Element
    var next: ElementNode?

    init(value: Element) {
        self.value = value
    }
}

func genFib() -> AnyIterator<Int> {
    var a = 1
    var b = 1
    return AnyIterator {
        let next = a
        a = b
        b = next + a
        return next
    }
}

var lst = List<Int>()
lst.push(10)
lst.push(13)
lst.push(23)

for value in lst.all() {
    print(value)
}

// Example usage of the genFib function.
for fib in genFib() {
    if fib >= 10 {
        break
    }
    print(fib)
}

All returns an iterator, which in Swift is an AnyIterator. The iterator will iterate over every element we want to iterate over until it reaches the end of the data structure.

Iteration doesn’t require an underlying data structure, and doesn’t even have to be finite! Here’s a function returning an iterator over Fibonacci numbers: it keeps running as long as yield keeps returning true.

Since List.all returns an iterator, we can use it in a regular for loop.

Packages like slices have a number of useful functions to work with iterators. For example, Collect takes any iterator and collects all its values into a slice.

Collect all values from the list into an array and print them:

let allValues = Array(lst.all())
print("all:", allValues)

Once the loop hits break or an early return, the iteration will stop automatically.