The Avid Gopher

Generators in Go

Some of you might know Python’s generators, which are a simple but efficient way of iterating through collections. By using generators we keep things simple and leave all the overhead of managing memory and any iterator state behind us.

Python Generators

Some of us might have seen some Python code like this before.

1
2
3
4
5
6
def rev_chars(s):
    for i in range(len(s)-1,-1,-1):
        yield s[i]

for c in rev_chars("Hello World"):
    print(c)

The first block defines a function called rev_chars which accepts a string called s as only parameter. Within that function we are walking down that string from end to beginning, char by char. With each step we are yielding the current char.

Functions yielding things are called generators.

In the lower block we are looping over the result of that function call to rev_chars (with Hello World as first parameter) and printing each of the results.

The nice thing about generators is, that we don’t need to allocate huge amounts of memory. By the use of yield we are just processing one char at a time respectively generating the result, like a fountain. Because of this we can reverse much, much longer strings respectively large data sets in general without the need of thinking about handling memory.

Go Generators

Go has no built-in generators and no yield. But Go has an at least equivalent construct, called channel. By the use of a channel we are able to imitate the behaviour of generators:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package main

import (
	"fmt"
	"unicode/utf8"
)

func rev_chars(s string) chan rune {
	c := make(chan rune)
	go func() {
		for i := len(s) - 1; i >= 0; i-- {
			runeValue, _ := utf8.DecodeRuneInString(s[i:])
			c <- runeValue
		}
		close(c)
	}()
	return c
}

func main() {
	for c := range rev_chars("Hello World") {
		fmt.Printf("%c", c)
	}
}