Go slice overwrite - go

Here is the source code:
package main
func main() {
testSlice()
}
func testSlice() {
slice := make([]int, 0)
//slice = append(slice, 1) ①
//slice = append(slice, 1, 2) ②
//slice = append(slice, 1, 2, 3) ③
//slice = append(slice, 1, 2, 3, 4) ④
slice = append(slice, 1, 2, 3, 4, 5) ⑤
//slice = append(slice, 1, 2, 3, 4, 5, 6) ⑥
slice2 := append(slice, 1)
slice3 := append(slice, 2)
for _, i := range slice2 {
print(i)
}
println()
for _, i := range slice3 {
print(i)
}
}
Expected output:
123451
123452
Actual output:
123452
123452
The output of ①~⑥ except ⑤ is as expected. But why ⑤ slice3 overwrites slice2?
Is the reason related to pointer or slice resize?

Check out this SO answer for a really helpful explanation of what a slice actually is before reading the rest of my answer. It might be easier to understand what's going on by printing out the actual slice headers. See the following example code (and go playground):
package main
import (
"fmt"
"reflect"
"unsafe"
)
func main() {
testSlice()
}
func testSlice() {
slice := make([]int, 0)
slice = append(slice, 1, 2, 3, 4, 5)
fmt.Printf("%+v\n", (*reflect.SliceHeader)(unsafe.Pointer(&slice)))
slice2 := append(slice, 1)
fmt.Printf("%+v\n", (*reflect.SliceHeader)(unsafe.Pointer(&slice2)))
slice3 := append(slice, 2)
fmt.Printf("%+v\n", (*reflect.SliceHeader)(unsafe.Pointer(&slice3)))
for _, i := range slice2 {
print(i)
}
println()
for _, i := range slice3 {
print(i)
}
}
This will print something like this:
&{Data:824634441776 Len:5 Cap:6}
&{Data:824634441776 Len:6 Cap:6}
&{Data:824634441776 Len:6 Cap:6}
This shows that all variables slice, slice2, and slice3 are pointing at the same data (Data which is a pointer to the first element of the slice) but the slice headers themselves are different. When you're performing your appends, you are modifying the underlying slice shared by all the variables, and storing new slice headers into new variables. The slice headers for slice2 and slice3 are looking at the same slice of data, so when you come along and perform your append for slice3, you're overwriting the 6th element in the underlying slice that is shared by all the variables.

Related

Merge pointer to a slice

playground
package main
import (
"fmt"
"math/rand"
)
func randoms() *[]int {
var nums []int = make([]int, 5, 5) //Created slice with fixed Len, cap
fmt.Println(len(nums))
for i := range [5]int{} {//Added random numbers.
nums[i] = rand.Intn(10)
}
return &nums//Returning pointer to the slice
}
func main() {
fmt.Println("Hello, playground")
var nums []int = make([]int, 0, 25)
for _ = range [5]int{} {//Calling the functions 5 times
res := randoms()
fmt.Println(res)
//nums = append(nums, res)
for _, i := range *res {//Iterating and appending them
nums = append(nums, i)
}
}
fmt.Println(nums)
}
I am trying to mimic my problem. I have dynamic number of function calls i.e randoms and dynamic number of results. I need to append all of the results i.e numbers in this case.
I am able to do this with iteration and no issues with it. I am looking for a way to do something like nums = append(nums, res). Is there any way to do this/any built-in methods/did I misunderstand the pointers?
I think you're looking for append(nums, (*res)...):
nums = append(nums, (*res)...)
playground
See this answer for more about ..., but in short it expands the contents of a slice. Example:
x := []int{1, 2, 3}
y := []int{4, 5, 6}
x = append(x, y...) // Now x = []int{1, 2, 3, 4, 5, 6}
Further, since you have a pointer to a slice, you need to dereference the pointer with *.
x := []int{1, 2, 3}
y := &x
x = append(x, (*x)...) // x = []int{1, 2, 3, 1, 2, 3}

How to sort a slice in go without mutating the original slice

In Golang, I am trying to sort a slice without mutating the original value
func main() {
originalArray := []int{4, 2, 1, 1, 2}
newArray := originalArray
sort.Ints(newArray)
fmt.Printf("%v", originalArray) // prints [1 1 2 2 4]
}
How can I sort a slice in golang without mutating the original value?
You need to make a copy of the original slice.
Use:
newArray := make([]int, len(originalArray))
copy(newArray, originalArray)
or:
newArray := append([]int{}, originalArray...)

Go subslice pointer reference

The result of s is [1, 2, 3], I thought that slices hold a reference to the underlying array. Isn't that the case?
package main
import (
"fmt"
)
func main() {
s := []int{1, 2, 3}
ss := s[1:]
ss = append(ss, 4)
for _, v := range ss {
v += 10
}
for i := range ss {
ss[i] += 10
}
fmt.Println(s)
}
I thought that slices hold a reference to the underlying array. Isn't that the case?
Yes it is. But you created an array with length 3 with this statement:
s := []int{1, 2, 3}
When you appended an element to ss, that required an allocation of a new, longer array. So you lost the link between ss and s with this statement:
ss = append(ss, 4)
You can verify that by running this example:
package main
import (
"fmt"
)
func main() {
s := []int{1, 2, 3}
ss := s[1:]
ss[0] += 5
ss = append(ss, 4)
ss[0] += 100
fmt.Println(s)
}
Which prints [1 7 3].
If you change your initialization of s to have a length greater than three, then no new array allocation will be required, and the link between s and ss will be maintained:
package main
import (
"fmt"
)
func main() {
s := make([]int, 3, 4)
s[0], s[1], s[2] = 1, 2, 3
ss := s[1:]
ss[0] += 5
ss = append(ss, 4)
ss[0] += 100
fmt.Println(s)
}
Output: [1 107 3]
The answer that theorizes that the problem is a range copy of the slice is incorrect, which can be shown with this example:
package main
import (
"fmt"
)
func main() {
s := make([]int, 3, 4)
s[0], s[1], s[2] = 1, 2, 3
ss := s[1:]
ss = append(ss, 4)
for i := range ss {
ss[i] += 10
}
fmt.Println(s)
}
Output: [1 12 13]
It seems like you've created a copy of slice s, and you've made changes to slice copy ss thinking that the changes would also be passed to the slice that the copy was made from.
The first for loop is also walking through the elements of slice ss, but is not actually doing anything with them, because range also creates a copy when providing you element values, so it doesn't actually do anything!
It does seem like you're trying to do the following:
Append the value 4 to slice s
Take each value from index 1 of slice to the end of the slice, and add 10
If that's the case, this should help you accomplish that:
package main
import (
"fmt"
)
func main() {
s := []int{1, 2, 3}
s = append(s, 4)
for i := range s {
if i == 0 {
continue
}
s[i] += 10
}
fmt.Println(s)
}
You can see this on the Go playground: Link

Golang remove elements when iterating over slice panics

I want delete some elements from a slice, and https://github.com/golang/go/wiki/SliceTricks advise this slice-manipulation:
a = append(a[:i], a[i+1:]...)
Then I coded below:
package main
import (
"fmt"
)
func main() {
slice := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
for i, value := range slice {
if value%3 == 0 { // remove 3, 6, 9
slice = append(slice[:i], slice[i+1:]...)
}
}
fmt.Printf("%v\n", slice)
}
with go run hello.go, it panics:
panic: runtime error: slice bounds out of range
goroutine 1 [running]:
panic(0x4ef680, 0xc082002040)
D:/Go/src/runtime/panic.go:464 +0x3f4
main.main()
E:/Code/go/test/slice.go:11 +0x395
exit status 2
How can I change this code to get right?
I tried below:
1st, with a goto statement:
func main() {
slice := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
Label:
for i, n := range slice {
if n%3 == 0 {
slice = append(slice[:i], slice[i+1:]...)
goto Label
}
}
fmt.Printf("%v\n", slice)
}
it works, but too much iteration
2nd, use another slice sharing same backing array:
func main() {
slice := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
dest := slice[:0]
for _, n := range slice {
if n%3 != 0 { // filter
dest = append(dest, n)
}
}
slice = dest
fmt.Printf("%v\n", slice)
}
but not sure if this one is better or not.
3rd, from Remove elements in slice, with len operator:
func main() {
slice := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
for i := 0; i < len(slice); i++ {
if slice[i]%3 == 0 {
slice = append(slice[:i], slice[i+1:]...)
i-- // should I decrease index here?
}
}
fmt.Printf("%v\n", slice)
}
which one should I take now?
with benchmark:
func BenchmarkRemoveSliceElementsBySlice(b *testing.B) {
for i := 0; i < b.N; i++ {
slice := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
dest := slice[:0]
for _, n := range slice {
if n%3 != 0 {
dest = append(dest, n)
}
}
}
}
func BenchmarkRemoveSliceElementByLen(b *testing.B) {
for i := 0; i < b.N; i++ {
slice := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
for i := 0; i < len(slice); i++ {
if slice[i]%3 == 0 {
slice = append(slice[:i], slice[i+1:]...)
}
}
}
}
$ go test -v -bench=".*"
testing: warning: no tests to run
PASS
BenchmarkRemoveSliceElementsBySlice-4 50000000 26.6 ns/op
BenchmarkRemoveSliceElementByLen-4 50000000 32.0 ns/op
it seems delete all elements in one loop is better
Iterate over the slice copying elements that you want to keep.
k := 0
for _, n := range slice {
if n%3 != 0 { // filter
slice[k] = n
k++
}
}
slice = slice[:k] // set slice len to remaining elements
The slice trick is useful in the case where a single element is deleted. If it's possible that more than one element will be deleted, then use the for loop above.
working playground example
while this is good answer for small slice:
package main
import "fmt"
func main() {
slice := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
k := 0
for _, n := range slice {
if n%3 != 0 { // filter
slice[k] = n
k++
}
}
slice = slice[:k]
fmt.Println(slice) //[1 2 4 5 7 8]
}
for minimizing memory write for first elements (for big slice), you may use this:
package main
import "fmt"
func main() {
slice := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
k := 0
for i, n := range slice {
if n%3 != 0 { // filter
if i != k {
slice[k] = n
}
k++
}
}
slice = slice[:k]
fmt.Println(slice) //[1 2 4 5 7 8]
}
and if you need new slice or preserving old slice:
package main
import "fmt"
func main() {
slice := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
s2 := make([]int, len(slice))
k := 0
for _, n := range slice {
if n%3 != 0 { // filter
s2[k] = n
k++
}
}
s2 = s2[:k]
fmt.Println(s2) //[1 2 4 5 7 8]
}

How to make sure slice is using a copy of another slice and not a reference to it?

I'm learning GO and I have a theoretical problem.
How do I use a copy of slice rather than a reference to it?
package main
import "fmt"
func main() {
// slice containing 3 items
slice1 := []int{1, 2, 3}
// make an empty slice
slice2 := make([]int, 2, 5)
// create slice3 by appending int 4 to slice2
slice3 := append(slice2, 4)
// print [0 0 4]
fmt.Println(slice3)
// copy elements of slice1 onto slice2
copy(slice2, slice1)
// print [1 2 3] [1 2] [1 2 4]; how to make sure slice3 is using a copy [0 0 4]?
fmt.Println(slice1, slice2, slice3)
}
problem playground link
I have came up with a potential solution, but it is pointless as it relies on slice3 being created empty and slice2 being copied via copy() onto slice3. Is there no shortcut?
package main
import "fmt"
func main() {
// slice containing 3 items
slice1 := []int{1, 2, 3}
// make an empty slice
slice2 := make([]int, 2, 5)
// create slice3, copy slice2 and append int 4 to slice3
slice3 := make([]int, 2)
copy(slice3, slice2)
slice3 = append(slice3, 4)
// print [0 0 4]
fmt.Println(slice3)
// copy elements of slice1 onto slice2
copy(slice2, slice1)
// print [1 2 3] [1 2] [0 0 4];
fmt.Println(slice1, slice2, slice3)
}
solution playground link
EDIT:
I've read that there is a peculiar behaviour that in this naive example would work as a solution (see below). However, in any other case it would not work.
Basically, if the empty slice is created without the size of an underlying array being specified, GO's append function provides a copy of that array, otherwise, if there is room to grow, append will return a slice that references the original array.
Note: the only change is slice2 := make([]int, 2, 5) into slice2 := make([]int, 2)
package main
import "fmt"
func main() {
// slice containing 3 items
slice1 := []int{1, 2, 3}
// make an empty slice
slice2 := make([]int, 2)
// create slice3 by appending int 4 to slice2
slice3 := append(slice2, 4)
// print [0 0 4]
fmt.Println(slice3)
// copy elements of slice1 onto slice2
copy(slice2, slice1)
// print [1 2 3] [1 2] [1 2 4]; how to make sure slice3 is using a copy [0 0 4]?
fmt.Println(slice1, slice2, slice3)
}
playground with a wanted behaviour
So the question becomes: Is it possible to replicate the behaviour of the above, when the slice we are appending to points to an array with specified size and room to grow?
EDIT 2:
I think there is some confusion as to what I want to achieve.
How to get the result of the second call while passing a slice in the format used in the first call?
package main
import "fmt"
func main() {
fmt.Println("s3 references an array of s1")
worker(make([]int, 2, 5))
fmt.Println("\ns3 copies an array of s1")
worker(make([]int, 2))
}
func worker(s1 []int) {
s2 := []int{1, 2, 3}
fmt.Println(s1)
s3 := append(s1, 4)
fmt.Println(s3)
copy(s1, s2)
fmt.Println(s3)
}
playground
A few people commented that I was not clear enough last night. So I would like to clarify and provide an answer that I arrived at with the help of #CoreyOgburn and #JimB
I was learning about slices in GO and I found an inconsistency, that led me to believe I'm doing something wrong. While hardly a real life example I found the following a good example of the copy and append functionality.
package main
import "fmt"
func main() {
fmt.Println("s3 references an array of s1")
// we pass a slice of length 2 and capacity 5
worker(make([]int, 2, 5))
fmt.Println("\ns3 copies an array of s1")
// we pass a slice of lenght 2 and capacity 2
worker(make([]int, 2))
}
func worker(s1 []int) {
// create new slice for future use
s2 := []int{1, 2, 3}
fmt.Println(s1)
// create a new slice by appending a value to a slice passed into this function
s3 := append(s1, 4)
// s3 holds whatever was passed into this function + int 4, that we just appended
fmt.Println(s3)
// copy contents of s2 onto s1
copy(s1, s2)
// if s1 had spare capacity when it was passed i.e. make([]int, 2, 5) s3 will be referencing the same array as s1, hence s3 will now hold the same values as s1
// if s1 capacity was the same as its length i.e. make([]int, 2) s3 will be referencing a new array after append(), hence copy has no effect on the values of s3
fmt.Println(s3)
}
#JimB posted a comment with a link to a blog post explaining how slices work, which is a great read if you are learning the language.
What is most important in the section A possible "gotcha" there is an explanation of a 'fix' to a real life scenario, that can be extrapolated as a fix the inconsistency in my example. (create a copy of a passed slice and use that instead)
Playground

Resources