go · level 3 · unit 1 · lesson 3
- meet it
- guess first
- your turn
- fix it
- 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.
guess first
No goroutines here, only closures saved for later. What prints?
Guess before you run it. Wrong guesses are how you learn, and nothing is marked.
package main
import "fmt"
func main() {
var calls []func()
for i := 1; i <= 3; i++ {
calls = append(calls, func() { fmt.Print(i, " ") })
}
for _, call := range calls {
call()
}
}
your turn
Your turn. Switch on the lights, one room each.
Name a third room. Then fill the blank so each round of the loop gets its own copy of i.
output
press run to see what happens
fix it
The menu runs off the end. Can you fix it?
output
press run to see what happens
- The panic says index 3 is out of range. Where could
ihave become 3? - The loop stops when
ireaches 3. The goroutines run afterwards and all read that one sharedi. - Add
i := ias the first line inside the loop, so each goroutine captures its own copy.
nailed it
you dodged the loop variable trap. :D
- A closure captures the variable itself, not its value.
- In Go 1.16 every round of a loop shares one variable.
- Pass the value in, or shadow it with
i := i.
new words in your collection
- capture
- shadowing
This lesson isn’t saved. A free account remembers every lesson you finish.