How Appending to Go Slices Can Unexpectedly Change Other Slices

Arrays in Go are different from arrays in other languages—they have a fixed size that cannot be changed. For dynamic arrays, Go uses slices instead. You can find basic information about arrays and slices here. While appending new elements to a slice is allowed and will increase the size when needed, this operation can sometimes unexpectedly modify elements in other slices.

Example

package main

import "fmt"

func main() {
	slice1 := []int{1, 2, 3}
	slice2 := append(slice1, 4)
	fmt.Println("slice2", slice2)
	slice3 := append(slice2, 5)
	fmt.Println("slice3", slice3) // [1 2 3 4 5]
	slice4 := append(slice2, 6)
	fmt.Println("slice4", slice4) // [1 2 3 4 6]
	fmt.Println("slice3", slice3) // [1 2 3 4 6]
}

Go Playground link: https://go.dev/play/p/MHltdaxLziE

Graph

Go Slice

Explanation

Slices in Go are designed as a view into an underlying array, with both capacity and length properties. The length determines how many elements can be accessed in the slice. In line 6, slice1 is declared with an underlying [3]int array to store the three elements.

When we append element 4 to slice1 in line 7, it exceeds the 3-element limit of the original array. The Go runtime creates a new array with double the capacity ([6]int) and appends 4 to create slice2. At this point, the array has a capacity of 6, meaning pointers for the fifth and sixth elements already exist.

In line 9, element 5 is appended to slice2’s underlying array, using the fifth position to create slice3.
In line 11, element 6 is appended to slice2’s underlying array, also using the fifth position to create slice4.

Since both operations in lines 9 and 11 are modifying the same memory location in the same underlying array, the append operation in line 11 unintentionally updates slice3, even though slice3 isn’t directly used in that operation.

Workaround

To avoid this issue, use the copy function to create a new slice with its own underlying array.

The copy function copies the values from slice2 to slice3, creating a new underlying array for slice3 that’s independent of slice2’s array.

package main

import "fmt"

func main() {
	slice1 := []int{1, 2, 3}
	slice2 := append(slice1, 4)
	fmt.Println(slice2)
	slice3 := make([]int, len(slice2))
	copy(slice3, slice2)
	slice3 = append(slice3, 5)
	fmt.Println("slice3", slice3) // [1 2 3 4 5]
	slice4 := append(slice2, 6)
	fmt.Println("slice4", slice4) // [1 2 3 4 6]
	fmt.Println("slice3", slice3) // [1 2 3 4 5]
}

Go Playground link: https://go.dev/play/p/sPbG-dsiT9x