×

go · level 3 · unit 1 · lesson 3

  1. meet it
  2. guess first
  3. your turn
  4. fix it
  5. nailed it

Tap a green word if you get lost.

meet it

In Go 1.16 every closure in a loop shares one loop variable.

A closure does not take a snapshot of the variables it uses. It captures them: it holds on to the variable itself, and sees whatever is inside at the moment it runs. In Go 1.16 a for loop reuses one variable for every round.

Start goroutines in a loop that use i without being handed it, and they often run after the loop has moved on. They all see the same late value, or the value that ended the loop, one past the end of your slice.

There are two cures. Pass the value in as an argument, as you did last lesson, or write i := i as the first line of the loop. That makes a brand-new i for this round only, hiding the outer one. This is called .

package main
import (
	"fmt"
	"strings"
	"sync"
)
func main() {
	names := []string{"Ada", "Grace", "Linus"}
	badges := make([]string, len(names))
	var wg sync.WaitGroup
	for i := range names {
		i := i
		wg.Add(1)
		go func() {
			defer wg.Done()
			badges[i] = "Hello, " + names[i]
		}()
	}
	wg.Wait()
	fmt.Println(strings.Join(badges, " | "))
}

Go 1.22 gave every round its own variable, but plenty of code you will read was written before then.