Why does the slice a
remain the same? Does append()
generate a new slice?
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) }
Output:
[0 1 2 3 4 5 6 100] [0 1 2 3 4 5 6]
Since slices are dynamically-sized, you can append elements to a slice using Golang's built-in append method. The first parameter is the slice itself, while the next parameter(s) can be either one or more of the values to be appended.
append( ) function and spread operatorTwo slices can be concatenated using append method in the standard golang library.
There is no way in Golang to directly append an array to an array. Instead, we can use slices to make it happen. The following characteristics are important to note: Arrays are of a fixed size.
The append built-in function appends elements to the end of a slice. If it has sufficient capacity, the destination is resliced to accommodate the new elements. If it does not, a new underlying array will be allocated. Append returns the updated 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]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With