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 (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.)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.
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)
}