Golang slice append gotcha

Slices in Go are very convenient way to deal with dynamic arrays. However, they don’t always behave as one may expect.

TL;DR: here is link to SO question describing problem and solution.

I guess that if you’re reading this, you’re already somehow familiar with Go, so I won’t introduce it in any way and I’ll focus on slices only.

Slices in Go are very useful Go’s arrays abstraction. Both slices and arrays have typed value, but arrays have also defined static length, therefore they’re usually not that useful, as You can’t append to them, cut them and in general — manipulate use them in friendly way. What arrays lack (by design) is provided by slices.

One of the most used method is build-in append function:

As first argument it takes slice we want append to and as second — element we want to be appended. It doesn’t modify the slice provided as first argument. Instead, it returns new slice.

Looks easy, right? 
But there is one gotcha:

You can run this example here https://play.golang.org/p/G3mmXSNEdY and see, that the sliceFromLoop will print always last value appended to it!

Why all previous new slices returned by append function are modified by last append?

Because Go append mechanic manipulates underlying array all the time and all new slices and their values are based on that array too.

That means, that creating new slice variables based on append mechanic can lead to very hard to discover bugs and problems.

What’s the solution?

  • use append only to append new value to given slice, not to create new one:
someSlice = append(someSlice, newElement)
  • if you want to create new slice based on old one with some appended value, always copy it first (example function credits go to SO user):
func copyAndAppend(i []int, vals ...int) []int {
j := make([]int, len(i), len(i)+len(vals))
copy(j, i)
return append(j, vals...)

or to just create a shallow copy of old slice:

newSlice := append(T(nil), oldSlice...)

It’s not very often used pattern. I found that problem after few years of writing production code in Go and probably that’s why it’s even more confusing and frustrating.

More info about slices:
very useful website about Go’s gotchas, including mentioned one