Go 1.22 changed for-loop variable semantics

The first change listed in the go 1.22 release changes the semantics of for loops in order to fix unexpected behavior that has bit me (and probably countless others) multiple times:

Previously, the variables declared by a “for” loop were created once and updated by each iteration. In Go 1.22, each iteration of the loop creates new variables, to avoid accidental sharing bugs.

Here is a simple demonstration that prints different results in go 1.21 and go 1.22:

package main

import (
    "fmt"
)

func main() {
    var funcs []func()
    for i := 0; i < 2; i++ {
        funcs = append(funcs, func() {fmt.Printf("%d\n", i)})
    }
    for _, f := range funcs {
        f()
    }
}

In Go 1.22, this works as most programmers would expect, printing 0, 1. The closure uses the value of i at the time that the closure is created. This variable is a different one for each iteration of the loop.

In Go 1.21 and earlier, it printed 2, 2.
The variable i was created once, and the closures that used it used it by reference. Since the closures were executed after the execution of the loop, i had the value “2”.

I searched the forum for a discussion of the old behavior, and found this:

Unfortunately, I cannot add a comment there to note the change in behavior.

Yeah - this is one of those “breaking” changes that will probably only have the side effect of fixing subtle bugs.