This is the argument in go1.12.3 linux/amd64
.
Added two closures to the slice s
containing the function through the variable x
. The closure is obtained by a method with a pointer receiver of type T
. Added two closures to the slice s containing the function through the variable x. The closure is obtained by a method with a pointer receiver of type T.
package main
import "fmt"
type T struct {
X int
}
func (t *T) f() func() {
return func() { fmt.Println(t.X) }
}
func good() {
s := []func(){}
x := &T{1}
s = append(s, x.f())
x = &T{2}
s = append(s, x.f())
s[0]() // => 1
s[1]() // => 2
}
func bad() {
s := []func(){}
x := T{1}
s = append(s, (&x).f()) // Even if it rewrites to append(s, x.f()), the result is the same.
x = T{2}
s = append(s, (&x).f())
s[0]() // => 2 WHY!?
s[1]() // => 2
}
func main() {
good()
bad()
}
https://play.golang.org/p/j-818FZELQb
Of the above two functions, good()
works as expected. In good s[0]()
executes T{1}.f()()
, and s s[1]()
executes T{2}.f()()
. However, bad()
runs s[0]()
and s[1]()
run T{2}.f()()
. Even though I added the element to the slice without overwriting it!
I have no idea what caused this behavior. Didn't the variable x
have a value of type T
instead of a pointer to a value of type T
? Or do I not know the call specification of append?
Please give me some advice.
Because in bad you have just one variable x
of type T and one variable has just one address and thus your slice s contains always the same function working on the same variable which has value 2 when you start calling the functions.
(In good you have two different variables of type T. You reuse a variable (x) of type *T but you still have two different T with different values.)