Hacker News new | ask | show | jobs
by throwaway894345 1655 days ago
The problem with using channels is that these require multiple goroutines and locking for a problem that's inherently singlethreaded. Instead, you can define an iterator as a function that returns a function:

    func intSliceIter(ints []int) func() (int, bool) {
        i := 0
        return func() (int, bool) {
            if i < len(ints) {
                ret := ints[i]
                i++
                return ret, true
            }
            return 0, false
        }
    }

    iter := intSliceIter([]int{0, 1, 2, 3, 4})

    for x, ok := iter(); ok; x, ok = iter() {
        fmt.Println(x)
    }
Of course, there's not much benefit to a SliceIter; this is a contrived example, but you can apply this pattern in more complicated cases as well. Similarly, you can define an iterator as an interface (which is similar to bufio.Scanner and a few others in the standard library—a closure is an object is a closure):

    type IntIter interface {
        Next() (int, bool)
    }

    type IntSliceIter struct {
        Cursor int
        Ints []int
    }

    func (isi *IntSliceIter) Next() (int, bool) {
        if isi.Cursor < len(isi.Ints) {
            ret := isi.Ints[isi.Cursor]
            isi.Cursor++
            return ret, true
        }
        return 0, false
    }