Final notes on for loop variable scope

June 5, 2024

For statements with for clause

… The variable used by the first iteration is declared by the init statement. The variable used by each subsequent iteration is declared implicitly before executing the post statement and initialized to the value of the previous iteration’s variable at that moment.

So let’s break this down. In fact, the pseudo code I showed you during my earlier breakdown really more closely demonstrated the behavior prior to Go 1.22. So let’s be a bit more explicit with variable scoping this time.

i := 0           // initial statement
if i < 3 {       // i == 0, 0 < 3 so execute the block
	fmt.Println(i) // Prints 0
	{
		i := i           // New `i` variable scoped to the new loop iteration
		i++              // Post statement
		if i < 3 {       // i == 1, 1 < 3 so execute the block
			fmt.Println(i) // Prints 1
			{
				i := i           // New `i` variable scoped to the new loop iteration
				i++              // Post statement
				if i < 3 {       // i == 2, 2 < 3 so execute the block
					fmt.Println(i) // Prints 2
					{
						i := i // New `i` variable scoped to the new loop iteration
						i++ // Post statement
						if i < 3 {       // i == 3, 3 is not less than 3, so the block does not execute, and the loop ends
							/* not executed */
						}
					}
				}
			}
		}
	}
}

That’s kind of a mess. If it doesn’t make sense, don’t worry too much about it. This isn’t a literal representation of what’s happening (each loop iteration doesn’t create a nested scope—that’s just the easiest way to write the pseudo code).

var prints []func()
for i := 0; i < 5; i++ {
	prints = append(prints, func() { println(i) })
	i++
}
for _, p := range prints {
	p()
}

prints

1
3
5

Prior to [Go 1.22], iterations share one set of variables instead of having their own separate variables. In that case, the example above prints

6
6
6

Quotes from The Go Programming Language Specification Language version go1.22 (Feb 6, 2024)


Share this

Direct to your inbox, daily. I respect your privacy .

Unsure? Browse the archive .

Related Content


Unique loop variables and the Go 1.22 fix

If you haven't joined me for a livestream in the past, consider [joining today](https://youtube.com/live/BYOsoZ5ywx8)! I'll be doing some refactoring, and adding new features to [Kivik](https://kivik.io/), with TDD. For statements with for clause … Each iteration has its own separate declared variable (or variables) [Go 1.22]. This seemingly simple sentence carries a lot of weight. So that’s what we’ll be talking about today. Prior to Go 1.22 (which only came out earlier this year!


Iteration over strings

For statements with range clause … For a string value, the “range” clause iterates over the Unicode code points in the string starting at byte index 0. On successive iterations, the index value will be the index of the first byte of successive UTF-8-encoded code points in the string, and the second value, of type rune, will be the value of the corresponding code point. If the iteration encounters an invalid UTF-8 sequence, the second value will be 0xFFFD, the Unicode replacement character, and the next iteration will advance a single byte in the string.


Iteration over arrays and slices

For statements with range clause … For an array, pointer to array, or slice value a, the index iteration values are produced in increasing order, starting at element index 0. If at most one iteration variable is present, the range loop produces iteration values from 0 up to len(a)-1 and does not index into the array or slice itself. For a nil slice, the number of iterations is 0. From this above paragraph, we can be assured that ranging over an array, pointer to array, or slice, will operate in a defined order–from index 0, upward.

Get daily content like this in your inbox!

Subscribe