Three new books, Go Optimizations 101, Go Details & Tips 101 and Go Generics 101 are published now. It is most cost-effective to buy all of them through this book bundle in the Leanpub book store.

strings.Builder iteration variables in traditional 3-clause for;; loops are implemented incorrectly

Go 1.22 changed the semantics of for loops, including both for-range loops and traditional 3-clause for ..; ..; .. {...} loops. For details, please read for Loop Semantic Changes in Go 1.22: Be Aware of the Impact.

In my honest opinion, the overall impact of the new semantics of for-range loops is positive, while the overall impact of the new semantics of traditional 3-clause for;; loops is negative. The above referenced article shows the all the bad effects of the semantic change of traditional 3-clause for;; loops I have found so far.

One fact is that, currently, the latest official Go toolchain doesn't implement the new semantics correctly for some cases in which strings.Builder values are used as (freshly-declared) iteration variables of traditional 3-clause for;; loops. The following example shows one such case:

package main

import (
	"fmt"
	"strings"
)

func foo() string {
	for b, i := (strings.Builder{}), byte('a'); ; i++ {
		b.WriteByte(i) // not panic
		if i == 'z' {
			return b.String()
		}
	}
}

func bar(callback func(*strings.Builder)) string {
	for b, i := (strings.Builder{}), byte('a'); ; i++ {
		b.WriteByte(i) // panics here
		callback(&b)
		if i == 'z' {
			return b.String()
		}
	}
}

func main() {
	fmt.Println(foo())
	debugProcess := func(pb *strings.Builder) {
		//fmt.Println(pb.String()) // do nothing
	}
	fmt.Println(bar(debugProcess))
}

The above example program doesn't panic with official Go toolchain versions prior to v1.22:

$ gotv 1.21 run main.go
[Run]: $HOME/.cache/gotv/tag_go1.21.12/bin/go run main.go
abcdefghijklmnopqrstuvwxyz
abcdefghijklmnopqrstuvwxyz

(GoTV is a tool used to manage and use multiple coexisting installations of official Go toolchain versions harmoniously and conveniently.)

However, when with official Go toolchain v1.22+, the example program panics:

$ gotv 1.22 run main.go
[Run]: $HOME/.cache/gotv/tag_go1.22.5/bin/go run main.go
abcdefghijklmnopqrstuvwxyz
panic: strings: illegal use of non-zero Builder copied by value

The behaviors of the foo and bar functions should be consistent with each other, but official Go toolchain v1.22+ fail to make the guarantee. In fact, by the Go 1.22+ new semantics, both of the foo and bar functions should produce a panic, because nocopy values should not be used as (freshly declared) iteration variables since Go 1.22.

Nite: the go vet command provided in Go toolchain 1.22 and 1.23 versions failed to warn on such cases.

The Go core team refused to fix this bug. You should be aware of this bug.


(more articles ↡)

The Go 101 project is hosted on Github. Welcome to improve Go 101 articles by submitting corrections for all kinds of mistakes, such as typos, grammar errors, wording inaccuracies, description flaws, code bugs and broken links.

If you would like to learn some Go details and facts every serveral days, please follow Go 101's official Twitter account @zigo_101.

Tapir, the author of Go 101, has been on writing the Go 101 series books and maintaining the go101.org website since 2016 July. New contents will be continually added to the book and the website from time to time. Tapir is also an indie game developer. You can also support Go 101 by playing Tapir's games (made for both Android and iPhone/iPad):
Individual donations via PayPal are also welcome.

Articles in this book: