Here is a code snippet from some Go book.
func incr(s []int) {
s = append(s, 0)
for i := range s {
s[i]++
}
}
func main() {
s1 := []int{1, 2}
s2 := s1
s2 = append(s2, 3)
incr(s1)
incr(s2)
fmt.Print(s1, s2) // "[1, 2][2, 3, 4]"
}
I don't understand why the result is "[1, 2][2, 3, 4]".
Based on two points from https://golang.org/doc/effective_go#slices:
Slices hold references to an underlying array, and if you assign one slice to another, both refer to the same array
If a function takes a slice argument, changes it makes to the elements of the slice will be visible to the caller, analogous to passing a pointer to the underlying array
Here is what I imagined should happen:
At first, both s1 and s2's have the same underlying array [1, 2]
After appending 3 to s2, the underlying array changed to [1, 2, 3]. But s1 still only see [1, 2]
After incr(s1), s1 is appended 0 and all item incremented, resulting s1 into [2, 3, 1]. The appending also changed the underlying array, so s2 now see [2, 3, 1]
After incr(s2), s2 is appended 0 and all item incremented, resulting s2 into [3, 4, 2, 1]. The increment also affected underlying array, so now s1 see [3, 4, 2]
So the result printed should be [3, 4, 2][3, 4, 2, 1]
I obviously have huge mistake understanding slice in Go. Please tell me where I am wrong. It seems my reasoning is in accord with slice's behavior. (I know appending to an insufficient capacity slice will also reallocate an underlying array, but don't know how to fit it into this).
Let's analyze this program step by step:
s1 := []int{1, 2} // s1 -> [1,2]
s2 := s1 // s1, s2 -> [1,2]
The next operation is:
s2 = append(s2, 3)
This may allocate a new backing array if the capacity of the underlying array is not sufficient. In this case, it will do that to give:
s1 -> [1,2]
s2 -> [1,2,3]
Then incr(s1) will append a new element to s1, increment values, but the resulting slice will not be assigned to s1 in main, so still:
s1 -> [1,2]
s2 -> [1,2,3]
incr(s2) will do the same, but this time, the backing array has the capacity to hold the appended zero, so the increment operation increments the values, but the new slice is never assigned to s2 in main. So, s2 still has 3 elements:
s1 -> [1,2]
s2 -> [2,3,4]
The backing array of s2 has one more element in it, but s2 does not include that element.
Hi everyone I hope you guys doing well, I was reading the copy build-in function, and I was wondering what was the difference between straight assign value from arr to tmp and copy the value from arr to tmp? since both will have the same result both value and cap and length of arr slice. here's the golang playground.
package main
import "fmt"
func main() {
arr := []int{1, 2, 3}
tmp := make([]int, len(arr))
copy(tmp, arr)
fmt.Println(tmp)
fmt.Println(arr)
fmt.Println(cap(tmp))
fmt.Println(len(tmp))
tmp2 := arr
fmt.Println(tmp2)
fmt.Println(cap(tmp2))
fmt.Println(len(tmp2))
}
the result is,
[1 2 3]
[1 2 3]
3
3
[1 2 3]
3
3
if they had a difference what will it be and can you explain it with an example, please and thank you for your time and consideration :)
The backing array of tmp is a copy of the the backing array from arr.
The slices tmp2 and arr share the same backing array.
See this code:
arr := []int{1, 2, 3}
tmp := make([]int, len(arr))
copy(tmp, arr)
tmp2 := arr
arr[0] = 22
fmt.Println(arr) // prints [22 2 3]
fmt.Println(tmp) // prints [1 2 3]
fmt.Println(tmp2) // prints [22 2 3]
Notice how changing an element in arr also changed in element in tmp2, but not tmp.
As Muffin already answered, slice data structure is a pointer to an array. So if you assign to a new variable, it is still a pointer to the same array. That's why, any changes you make to the slice is reflected back to the array it represents.
Also, a new slice is initialised with a small capacity (array length). Once it is fully occupied, it has to create a new array and copy all its content from the old to new array.
The return value of slice2 of this code is [[1 1][1 1]].
And that got me confuse because I was expecting [[0 0][1 1]].
I can't figure it out why is returning [[1 1][1 1]] and not [[0 0][1 1]].
I would appreciate if someone can explain that. Thanks.
slice := []int{0, 0}
slice2 := [][]int{}
for i := range slice {
slice[0] = i
slice[1] = i
slice2 = append(slice2, slice)
}
fmt.Println(slice2)
You can check the code in this link
play.golang.org
A slice is a descriptor of an array segment. It consists of a pointer to the array, the length of the segment, and its capacity (the maximum length of the segment).
In your case your are appending a slice(pointer) not the slice's values. To get the desired result you have to declare a new slice in each iteration and append the new slice to slice2.
func main() {
slice := []int{0, 0}
slice2 := [][]int{}
for i := range slice {
ns := []int{0, 0}
ns[0] = i
ns[1] = i
slice2 = append(slice2, ns)
}
fmt.Println(slice2) // Outputs: [[0 0] [1 1]]
}
Playground
When you range over the slice, it is not giving you the VALUES of the slice, it is giving you the indexes. This for loop is equivalent to for i:=0; i < len(slice); i++ {}. So by the time it gets through iteration, both values are in slice are 1, and you have appended it to slice2 twice.
If you wanted the values of the slice, you would need to do for i, v := range slice {} which would give you both the indexes and the values. You could use _ in place of i if you didn't want the index.
Also note that appending slice twice like this appends the same exact slice because slices are allocated as pointers on the heap and it is therefore a pointer to the slice. Thus slice2[0] == slice2[1] because it is the same exact slice.
The problem is that what you append to slice2 is a reference to slice, not the values contained in slice at the time of the call.
This means that at the end, slice2 contains two pointers to the same slice. Any changes to the slice variable will be reflected to slice2 and vice-versa: they point to the same location in memory.
As a proof, just do:
slice2[0][1] = 2
fmt.Println(slice2)
fmt.Println(slice)
You will get:
[[1 2] [1 2]]
[1 2]
I'm trying to combine the slice [1, 2] and the slice [3, 4]. How can I do this in Go?
I tried:
append([]int{1,2}, []int{3,4})
but got:
cannot use []int literal (type []int) as type int in append
However, the documentation seems to indicate this is possible, what am I missing?
slice = append(slice, anotherSlice...)
Add dots after the second slice:
// vvv
append([]int{1,2}, []int{3,4}...)
This is just like any other variadic function.
func foo(is ...int) {
for i := 0; i < len(is); i++ {
fmt.Println(is[i])
}
}
func main() {
foo([]int{9,8,7,6,5}...)
}
Appending to and copying slices
The variadic function append appends zero or more values x to s
of type S, which must be a slice type, and returns the resulting
slice, also of type S. The values x are passed to a parameter of
type ...T where T is the element type of S and the respective
parameter passing rules apply. As a special case, append also accepts
a first argument assignable to type []byte with a second argument of
string type followed by .... This form appends the bytes of the
string.
append(s S, x ...T) S // T is the element type of S
s0 := []int{0, 0}
s1 := append(s0, 2) // append a single element s1 == []int{0, 0, 2}
s2 := append(s1, 3, 5, 7) // append multiple elements s2 == []int{0, 0, 2, 3, 5, 7}
s3 := append(s2, s0...) // append a slice s3 == []int{0, 0, 2, 3, 5, 7, 0, 0}
Passing arguments to ... parameters
If f is variadic with final parameter type ...T, then within the
function the argument is equivalent to a parameter of type []T. At
each call of f, the argument passed to the final parameter is a new
slice of type []T whose successive elements are the actual arguments,
which all must be assignable to the type T. The length of the slice is
therefore the number of arguments bound to the final parameter and may
differ for each call site.
The answer to your question is example s3 := append(s2, s0...) in the Go Programming Language Specification. For example,
s := append([]int{1, 2}, []int{3, 4}...)
Nothing against the other answers, but I found the brief explanation in the docs more easily understandable than the examples in them:
func append
func append(slice []Type, elems ...Type) []Type 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. It is therefore necessary to store the
result of append, often in the variable holding the slice itself:
slice = append(slice, elem1, elem2)
slice = append(slice, anotherSlice...)
As a special case, it is legal to append a string to a byte slice,
like this:
slice = append([]byte("hello "), "world"...)
I would like to emphasize #icza answer and simplify it a bit since it is a crucial concept. I assume that reader is familiar with slices.
c := append(a, b...)
This is a valid answer to the question.
BUT if you need to use slices 'a' and 'c' later in code in different context, this is not the safe way to concatenate slices.
To explain, lets read the expression not in terms of slices, but in terms of underlying arrays:
"Take (underlying) array of 'a' and append elements from array 'b' to
it. If array 'a' has enough capacity to include all elements from 'b'
- underlying array of 'c' will not be a new array, it will actually be array 'a'. Basically, slice 'a' will show len(a) elements of
underlying array 'a', and slice 'c' will show len(c) of array 'a'."
append() does not necessarily create a new array! This can lead to unexpected results. See Go Playground example.
Always use make() function if you want to make sure that new array is allocated for the slice. For example here are few ugly but efficient enough options for the task.
la := len(a)
c := make([]int, la, la + len(b))
_ = copy(c, a)
c = append(c, b...)
la := len(a)
c := make([]int, la + len(b))
_ = copy(c, a)
_ = copy(c[la:], b)
I think it's important to point out and to know that if the destination slice (the slice you append to) has sufficient capacity, the append will happen "in-place", by reslicing the destination (reslicing to increase its length in order to be able to accommodate the appendable elements).
This means that if the destination was created by slicing a bigger array or slice which has additional elements beyond the length of the resulting slice, they may get overwritten.
To demonstrate, see this example:
a := [10]int{1, 2}
fmt.Printf("a: %v\n", a)
x, y := a[:2], []int{3, 4}
fmt.Printf("x: %v, y: %v\n", x, y)
fmt.Printf("cap(x): %v\n", cap(x))
x = append(x, y...)
fmt.Printf("x: %v\n", x)
fmt.Printf("a: %v\n", a)
Output (try it on the Go Playground):
a: [1 2 0 0 0 0 0 0 0 0]
x: [1 2], y: [3 4]
cap(x): 10
x: [1 2 3 4]
a: [1 2 3 4 0 0 0 0 0 0]
We created a "backing" array a with length 10. Then we create the x destination slice by slicing this a array, y slice is created using the composite literal []int{3, 4}. Now when we append y to x, the result is the expected [1 2 3 4], but what may be surprising is that the backing array a also changed, because capacity of x is 10 which is sufficient to append y to it, so x is resliced which will also use the same a backing array, and append() will copy elements of y into there.
If you want to avoid this, you may use a full slice expression which has the form
a[low : high : max]
which constructs a slice and also controls the resulting slice's capacity by setting it to max - low.
See the modified example (the only difference is that we create x like this: x = a[:2:2]:
a := [10]int{1, 2}
fmt.Printf("a: %v\n", a)
x, y := a[:2:2], []int{3, 4}
fmt.Printf("x: %v, y: %v\n", x, y)
fmt.Printf("cap(x): %v\n", cap(x))
x = append(x, y...)
fmt.Printf("x: %v\n", x)
fmt.Printf("a: %v\n", a)
Output (try it on the Go Playground)
a: [1 2 0 0 0 0 0 0 0 0]
x: [1 2], y: [3 4]
cap(x): 2
x: [1 2 3 4]
a: [1 2 0 0 0 0 0 0 0 0]
As you can see, we get the same x result but the backing array a did not change, because capacity of x was "only" 2 (thanks to the full slice expression a[:2:2]). So to do the append, a new backing array is allocated that can store the elements of both x and y, which is distinct from a.
append( ) function and spread operator
Two slices can be concatenated using append method in the standard golang library. Which is similar to the variadic function operation. So we need to use ...
package main
import (
"fmt"
)
func main() {
x := []int{1, 2, 3}
y := []int{4, 5, 6}
z := append([]int{}, append(x, y...)...)
fmt.Println(z)
}
output of the above code is: [1 2 3 4 5 6]
To concatenate two slices,
func main() {
s1 := []int{1, 2, 3}
s2 := []int{99, 100}
s1 = append(s1, s2...)
fmt.Println(s1) // [1 2 3 99 100]
}
To append a single value to a slice
func main() {
s1 := []int{1,2,3}
s1 := append(s1, 4)
fmt.Println(s1) // [1 2 3 4]
}
To append multiple values to a slice
func main() {
s1 := []int{1,2,3}
s1 = append(s1, 4, 5)
fmt.Println(s1) // [1 2 3 4]
}
Seems like a perfect use for generics (if using 1.18 or later).
func concat[T any](first []T, second []T) []T {
n := len(first);
return append(first[:n:n], second...);
}
append([]int{1,2}, []int{3,4}...) will work. Passing arguments to ... parameters.
If f is variadic with a final parameter p of type ...T, then within f the type of p is equivalent to type []T.
If f is invoked with no actual arguments for p, the value passed to p is nil.
Otherwise, the value passed is a new slice of type []T with a new underlying array whose successive elements are the actual arguments, which all must be assignable to T. The length and capacity of the slice is therefore the number of arguments bound to p and may differ for each call site.
Given the function and calls
func Greeting(prefix string, who ...string)
Greeting("nobody")
Greeting("hello:", "Joe", "Anna", "Eileen")
How can I join multiple slices of the same entity into one slice?
Or how do I push a new entity value into a slice of the entity?
The go-wiki has a collection of SliceTricks that you will find useful.
For example,
Append Slice
a = append(a, b...)
Insert Value
s = append(s, 0)
copy(s[i+1:], s[i:])
s[i] = x
Push Value
a = append(a, x)
References:
Go Programming Language Specification:
Slice types
Indexes
Slices
Making slices
Appending to and copying slices
Slices: usage and internals
The append builtin does both of that for you. Use it like:
a := []int{1, 2}
a = append(a, 3)
b := []int{4, 5}
a = append(a, b...)
// a now is []int{1, 2, 3, 4, 5}
If you need more information on how to use slices, I recommend reading Slices: usage and internals.