Unexpected Overwriting Issue with Go's "append" Function
In Go, the append function is frequently used to extend slices with additional elements. However, occasionally, unexpected behavior can occur when appending pointers to structures.
Consider the following code snippet:
import "fmt" type Foo struct { val int } func main() { var a = make([]*Foo, 1) a[0] = &Foo{0} var b = [3]Foo{Foo{1}, Foo{2}, Foo{3}} for _, e := range b { a = append(a, &e) } for _, e := range a { fmt.Printf("%v ", *e) } }
Intriguingly, instead of printing {0} {1} {2} {3}, the code outputs {0} {3} {3} {3}. This unexpected behavior stems from a fundamental aspect of Go's behavior:
Copies in for Range Loops
Go iterates over slices and arrays using for range loops, which create a copy of each element during iteration. In our code, the for range loop variable e represents a temporary copy of the array element b[i]. Unfortunately, we are then appending the address of this temporary variable to the slice a.
The Fix
To resolve this issue, we need to append the address of the original array element instead of the temporary copy. By modifying the for loop to use the index i, we ensure that the correct address is appended to the slice:
for i := range b { a = append(a, &b[i]) }
This change yields the expected output of {0} {1} {2} {3}.
Reason for the Behavior
Go's lack of true references contributes to this behavior. In C or Java, where references are used, the loop variable would directly reference the array element, allowing mutations via the loop variable. However, in Go, the loop variable is a distinct variable that holds a copy of the element, not a reference. Consequently, changes made to the loop variable are not reflected in the original array elements.
The above is the detailed content of Why Does Appending Pointers in Go's `append` Function Lead to Unexpected Overwriting?. For more information, please follow other related articles on the PHP Chinese website!