2D slices and arrays in Golang

Subtle, unexpected behaviour of Golang's slice "append"

Python leaky unittests Gotcha - Too Many MySQL Connections

OpenWorm - a long way to go

Open Source Software Presentation

I am a software engineer (lapsed physicist) who likes working with open source technology. I spend most of my time writing code in Python but I am currently also learning Go.

In Go, a slice describes a segment of an array. It consists of

  1. a pointer to the array
  2. the length of the segment
  3. its capacity (the maximum length of the segment).

go slice

When calling append on a slice, if the length of the slice after the append operation is larger than the underlying array (the capacity, in Go-speak), a new array is instantiated to hold the new data (The new array is is much larger, to accommodate more data being added). As a consequence, any other slice previously referring to the same underlying array now refers to a different array. This gives rise to some slightly strange behaviour, best shown in this example:

package main

import (
    "fmt"
)

func main() {
    a := []int{1,2,3}
    slice1 := a[:2]
    slice2 := a[:2]
    slice2 = append(slice2,4) // first append
    slice2[0] = 8 // underlying array still the same, both slice 1 and slice 2 have element 0 == 8
    fmt.Println("slice 1:", slice1) // slice 1: [8 2]
    fmt.Println("slice 2:", slice2) // slice 2: [8 2 4]
    slice2 = append(slice2,4) // array capacity now exceeded, new underlying array created
    slice2[0] = 3 // slice1 still has element 0 == 8, but slice 2 has element 0 == 3
    fmt.Println("slice 1:", slice1) // slice 1: [8 2]
    fmt.Println("slice 2:", slice2) // slice 2: [3 2 4 4]
}

At the begining, slice1 and slice2 above referred to the underlying array. When an append is performed on slice2 the underlying array still has sufficient capacity and so an in-place change to slice2 shows up in slice1. However, when the second append happens the underlying array is not big enough so slice2 refers to a new array with a larger capacity. Therefore, any changes to slice2 are "out of sync" with slice1.

This kind of thing is something you're unlikely to encounter too often, but is good to be aware of. In general, it seems like having slices refer to the same underlying array is probably best avoided, unless it's for a very specific reason.