Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Golang append an item to a slice

Tags:

go

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] 
like image 403
Pole_Zhang Avatar asked Nov 25 '13 14:11

Pole_Zhang


People also ask

How do I append in Slice?

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.

Can you append a slice to a slice Golang?

append( ) function and spread operatorTwo slices can be concatenated using append method in the standard golang library.

How do you add a value to an array in Golang?

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.

Does append create a new slice Golang?

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.


2 Answers

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.

like image 171
kostix Avatar answered Sep 26 '22 02:09

kostix


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] 
like image 21
Fred Foo Avatar answered Sep 26 '22 02:09

Fred Foo