I have the following code which causes a weird result. I cannot understand why:
func main() {
var s = []int{2, 3}
var s1 = append(s, 4)
var a = append(s1, 5)
var b = append(s1, 6)
fmt.Println(s)
fmt.Println(s1)
fmt.Println(a)
fmt.Println(b)
}
This then results in:
[2 3]
[2 3 4]
[2 3 4 6]
[2 3 4 6]
My question is: why a is not [2 3 4 5] but [2 3 4 6]? I know append to b changes a, but how. Is this a bug because I never changed a directly and obviously I don't want this happen?
Keep in mind that a slice is a structure of 3 fields.
a pointer to the underlying array
length of the slice
capacity of the slice
append() function 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. append() function creates a new slice, if the length the slice is greater than the length of the array pointed by the slice.
Related
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.
I'd like to make a copy of a slice of bytes, convert the type from [][4]byte to [][]byte. And I expect the followering code to return [[1 2 3 4] [2 3 4 5]], but actually got [[2 3 4 5] [2 3 4 5]].
package main
import "fmt"
func main() {
ids := [][4]byte{[4]byte{1, 2, 3, 4}, [4]byte{2, 3, 4, 5}}
var slices [][]byte
for _, id := range ids {
slices = append(slices, id[:])
}
fmt.Println(slices)
}
Why?
Why appending id[:] to the slice would overwrite the first element in the slice?
As said by Marc, you are appending a reference via id.
Therefore when you iterate, and id changes its value, your "slices" to which you are appending are getting re-evaluated with id new values.
To avoid this:
for i := range ids {
slices = append(slices, ids[i][:]) // here you are referring to the value at index i of ids
}
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 3 years ago.
Improve this question
I was trying to get hands-on in slice and I observed that its not behaving as expected.
I was trying to get the cartesian product of n arrays of diff size. The output is not correct.
here is my code
func main() {
sl1 := []int{1,2,3}
sl2 := []int{4}
sl3 := []int{5,6}
sl4 := []int{8,9}
sl := [][]int{sl1,sl2,sl3,sl4}
res := cartesianMain(sl)
fmt.Println(res)
}
func cartesianMain(a [][]int) [][]int {
res := [][]int{}
for i:=0;i<len(a[0]) ;i++{
res = append(res,[]int{a[0][i]})
}
for i:= 1;i<len(a) ;i++{
res = cartesianProduct(res,a[i])
}
return res;
}
func cartesianProduct(a [][]int, b []int) [][]int {
result := [][]int{}
for _,v1 := range b {
for _,v2 := range a {
result = append(result, append(v2,v1))
}
}
return result
}
actual output:
[[1 4 5 9] [2 4 5 9] [3 4 5 9] [1 4 6 9] [2 4 6 9] [3 4 6 9] [1 4 5 9] [2 4 5 9] [3 4 5 9] [1 4 6 9] [2 4 6 9] [3 4 6 9]]
expected output:
if you see the sl4's 1st element 8 is over written by 9.
correct answer will be :
[[1 4 5 8] [2 4 5 8] [3 4 5 8] [1 4 6 8] [2 4 6 8] [3 4 6 8] [1 4 5 9] [2 4 5 9] [3 4 5 9] [1 4 6 9] [2 4 6 9] [3 4 6 9]]
This is becuase of how append works. In Go, slice is a header that keeps 3 property: Len, Cap, and Ptr. Len is the length of the slice it self, Cap is the capacity of the undrlying array of the slice (the memory), and Ptr is the pointer to the array.
When append appends to a slice where there are no more space in the underlying array, it allocates a new underlying array with some more space than needed and copies the content of the orignal slice into it and then adds the new element.
And When append appends to a slice where Cap > Len, i.e. where there are still sufficent space in the already allocated memory, append keeps the underlying array and copies the element to add to the a[Len+1] (where a is the underlying array). This will cause problem when two or more slice share the underlying memory.
A rule of thumb is to frequently check the need of copying a slice to avoid undesired sharing of underlying array.
To fix the problem, change result = append(result, append(v2,v1)) to result = append(result, append([]int{}, append(v2, v1)...)).
See also: https://blog.golang.org/slices
Note 1: append([]int{},append(v2,v1...)) is a shortcut of copy and apply. More tricks for slice can be found at: https://github.com/golang/go/wiki/SliceTricks .
Note 2: In Go, nil is a valid value for slice. so you can get rid of the seperation of split in cartesianMain by setting res to []int{nil}.
Note 3: For better performance and less allocations, it is a good practise to set the capacity (or length) for known slice. In cartesianProduct, you can use result := make([][]int, 0, len(a)*len(b)).
Playground: https://play.golang.org/p/rLqDGWoTLKS
If I have a Python list, it is simple to duplicate the last element: l.append(l[-1]). Is there an elegant way to do this with Go slices?
The best I have so far is data = append(data, data[len(data) - 1]).
That is the proper way to do it.
In Go you can't use negative indices, so the index of the last element is len(data) -1.
And in Go append() is a builtin function and not a method of slices, and it returns a new slice value which you have to assign or store if you need the extended slice, so there's nothing you can make shorter in your code in general.
Note that if you were on to create your own type, you could attach methods to it which could simpify things. For example:
type ints []int
func (i *ints) append(es ...int) {
*i = append(*i, es...)
}
func (i *ints) appendLast() {
*i = append(*i, (*i)[len(*i)-1])
}
Using it:
is := ints{1, 2, 3}
fmt.Println(is)
is.append(4, 5) // Append some elements
fmt.Println(is)
is.append(is[len(is)-1]) // Append last element explicitly
fmt.Println(is)
is.appendLast() // Append last element using appendLast method
fmt.Println(is)
// You can also use it for []int values, with conversion:
is2 := []int{1, 2, 3}
(*ints)(&is2).appendLast()
fmt.Println(is2)
Output (try it on the Go Playground):
[1 2 3]
[1 2 3 4 5]
[1 2 3 4 5 5]
[1 2 3 4 5 5 5]
[1 2 3 3]
You can also use simple util functions without creating a new type:
func appendLast(i []int) []int {
return append(i, i[len(i)-1])
}
func appendLast2(i *[]int) {
*i = append(*i, (*i)[len(*i)-1])
}
func main() {
is := []int{1, 2, 3}
fmt.Println(is)
is = appendLast(is)
fmt.Println(is)
appendLast2(&is)
fmt.Println(is)
}
Output (try it on the Go Playground):
[1 2 3]
[1 2 3 3]
[1 2 3 3 3]
data = append(data, data[len(data) - 1])
Yes. It's the best way to do in Go. It's not possible to use a negative index in Go.
My question is about slice length and capacity. I'm learning about Go here: https://tour.golang.org/moretypes/11.
(My question was marked as a possible duplicate of this; however, this is not the case. My question is specifically about the cutting off the first few elements of a slice and the implications of that.)
Why does the line s = s[2:] decrease the capacity when s = s[:4] and s = s[:0] do not? The only difference I see is that there is a number before the colon in s = s[2:] while there is a number after the colon in the other two lines.
Is there any way to recover the first two elements that we cut off with s = s[2:]?
package main
import "fmt"
func main() {
s := []int{2, 3, 5, 7, 11, 13}
printSlice(s)
// Slice the slice to give it zero length.
s = s[:0]
printSlice(s)
// Extend its length.
s = s[:4]
printSlice(s)
// Drop its first two values.
s = s[2:]
printSlice(s)
}
func printSlice(s []int) {
fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s)
}
After clicking the Run button, we get the following.
len=6 cap=6 [2 3 5 7 11 13]
len=0 cap=6 []
len=4 cap=6 [2 3 5 7]
len=2 cap=4 [5 7]
You can read more about slices here. But I think this passage answers your question:
Slicing does not copy the slice's data. It creates a new slice value that points to the original array. This makes slice operations as efficient as manipulating array indices. Therefore, modifying the elements (not the slice itself) of a re-slice modifies the elements of the original slice.
So you cannot recover the slice data if you are assigning it to the same variable.
The capacity decrease is because by dropping the first 2 elements you are changing the pointer to the new slice (slices are referenced by the pointer to the first element).
How slices are represented in the memory:
make([]byte, 5)
s = s[2:4]
You can use a full slice expression:
package main
func main() {
s := []int{2, 3, 5, 7, 11, 13}
{ // example 1
t := s[:0]
println(cap(t) == 6)
}
{ // example 2
t := s[:0:0]
println(cap(t) == 0)
}
}
https://golang.org/ref/spec#Slice_expressions
The slices.Clip of "golang.org/x/exp/slices" could reduce the capacity of slice through Full slice expressions.
Clip removes unused capacity from the slice, returning s[:len(s):len(s)].
func main() {
s := []int{2, 3, 5, 7, 11, 13}
printSlice(s)
s = s[:4]
printSlice(s)
s = slices.Clip(s)
printSlice(s)
}
func printSlice(s []int) {
fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s)
}
len=6 cap=6 [2 3 5 7 11 13]
len=4 cap=6 [2 3 5 7]
len=4 cap=4 [2 3 5 7]
Playground