ataraskov.dev

About everything and nothing


The mystery of v := v

Somethings one can observe strange v := v in Go source code. And there is a reason behind it. Take this code from Go FAQ for example:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
func f() {
	done := make(chan bool)
	values := []int{"a", "b", "c"}
	for _, v := range values {
		v := v  // <-- Yeap, right here
		go func() {
			fmt.Println(v)
			done <- true
		}()
	}
	for range values {
		<-done
	}
}

 

Output of this code is predictable and contains all the values from values variable.:

c
b
a

But if we remove line 5, logic is broken:

c
c
c

This is simple case to remember, sure. Just use one of ‘workarounds’: assign value to inner variable (v := v), or use is an argument for the go routine ( go func(u ...) {...}(v)).

But there are more subtle ways to shoot yourself. By tests for example. Unfortenately tests are code as well, and can be false positive (snippet from Go Wiki):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
func TestFoo(t *testing.T) {
	testCases := []int{1, 2, 3, 4}
	for _, v := range testCases {
		// v := v // get new `v`, to fix
		t.Run("sub", func(t *testing.T) {
			t.Parallel()
			if v%2 != 0 {
				t.Fatal("odd v", v)
			}
		})
	}
}

 

And this test will pass :(. Sometimes odd numbers are even. And here only v := v appears to be applicable, as we can’t easily redefine t.Run() for pass extra arguments.

References:

Thu Jul 13, 2023 / 285 words / Golang Programming Gotchas