Golang append an item to a slice

In your example the slice argument of the Test function receives a copy of the variable a in the caller's scope.

Since a slice variable holds a "slice descriptor" which merely references an underlying array, in your Test function you modify the slice descriptor held in the slice variable several times in a row, but this does not affect the caller and its a variable.

Inside the Test function, the first append reallocates the backing array under the slice variable, copies its original contents over, appends 100 to it, and that's what you're observing. Upon exiting from Test, the slice variable goes out of scope and so does the (new) underlying array that slice references. (Jeff Lee is correct about that it's not what really happens, so the updated version follows; as he correctly states, this answer is correct, if maybe a bit too terse.)

Outside the Test function, a slice of length 7 and capacity 8 is allocated, and its 7 elements filled.
Inside the Test function, the first append sees the that the slice's capacity is still one element larger than its length — in other words, there is room for one more element to add without reallocation. So it "eats up" that remaining element and places 100 to it, after which it adjusts the length in the copy of the slice descriptor to become equal to the slice's capaticy. This does not affect the slice descriptor's in the caller's scope.

And that's what you're observing. Upon exiting from Test, the slice variable goes out of scope and so does the (new) underlying array that slice references.

If you want to make Test behave like append, you have to return the new slice from it — just like append does — and require the callers of Test to use it in the same way they would use append:

func Test(slice []int) []int {
    slice = append(slice, 100)

    fmt.Println(slice)

    return slice
}

a = Test(a)

Please read this article thoroughly as it basically shows you how to implement append by hand, after explaining how slices are working internally. Then read this.


Typical append usage is

a = append(a, x)

because append may either modify its argument in-place or return a copy of its argument with an additional entry, depending on the size and capacity of its input. Using a slice that was previously appended to may give unexpected results, e.g.

a := []int{1,2,3}
a = append(a, 4)
fmt.Println(a)
append(a[:3], 5)
fmt.Println(a)

may print

[1 2 3 4]
[1 2 3 5]

In order to make your code work without having to return the slice from Test, you can pass a pointer like this:

package main

import (
    "fmt"
)

var a = make([]int, 7, 8)

func Test(slice *[]int) {
    *slice = append(*slice, 100)

    fmt.Println(*slice)
}

func main() {

    for i := 0; i < 7; i++ {
        a[i] = i
    }

    Test(&a)

    fmt.Println(a)
}