DEV Community

Cover image for [Go] Slices which point to the same array may cause side effect
Julian-Chu
Julian-Chu

Posted on

[Go] Slices which point to the same array may cause side effect

Relationship between slice and array

A slice isn't an array, it's a struct which consists of a pointer to array element, and length of segment, and arr capacity(length). So you can consider it uses pointer and length to slice an array. When you manipulate a slice, it will return new slice, but the array under the hood may not allocate. This may cause side effect.

You can find the source code of slice here:

type slice struct {
    array unsafe.Pointer
    len   int
    cap   int
}

Let's write some code and draw a diagram to represent the concept.

s1:=[]int{1,2,3}
// cut slice : change len
s2:= s1[:2]
// cut slice : change len and pointer to array element
s3 := s1[1:3]

Alt Text

Potential issue -- manipulate the same array

Sometimes bug will happen when you manipulate slices both point to the same array. Like the following code, I just want to change the value in S2 only, but it affects s1 and s3, because they both point to the same array.

func main() {
    s1 := []int{1, 2, 3}    
    s2 := s1[:2]
    s3 := s1[1:3]
    fmt.Printf("%v\n%v\n%v\n", s1, s2, s3)
    fmt.Println("-----")

    s2[1] = 0
    fmt.Printf("%v\n%v\n%v\n", s1, s2, s3)
}

output:
[1 2 3]
[1 2]
[2 3]
----------
[1 0 3]
[1 0]
[0 3]

How to avoid?

  • just work on the last slice. If you don't need other slice, of course it has no risk, but if you can't, there's two function to copy new slice.

  • use built-in function "func copy(dst, src []Type) int",
    copy function will only copy elements, based on len of dst, from src

func main() {
    s1 := []int{1, 2, 3}
    s2 := make([]int,2)
    copy(s2, s1)
    s3 := make([]int,2)
    copy(s3, s1[1:])
    fmt.Printf("%v\n%v\n%v\n", s1, s2, s3)
    fmt.Println("-----")

    s2[1] = 0
    fmt.Printf("%v\n%v\n%v\n", s1, s2, s3)
}

output:
[1 2 3]
[1 2]
[2 3]
-----
[1 2 3]
[1 0]
[2 3]
  • use built-in function "func append(slice []Type, elems ...Type) []Type", when you append to empty slice, it will allocate the array and make a copy.
func main() {
    s1 := []int{1, 2, 3}
    s2 := append([]int{}, s1...)
    s3 := append([]int{}, s1[1:]...)
    fmt.Printf("%v\n%v\n%v\n", s1, s2, s3)
    fmt.Println("-----")

    s2[1] = 0
    fmt.Printf("%v\n%v\n%v\n", s1, s2, s3)
}
output:
[1 2 3]
[1 2]
[2 3]
-----
[1 2 3]
[1 0]
[2 3]

Personally, I prefer to use append, because it's shorter and less code, but append can also make another issue because of allocating. Let's discuss the allocating mechanism in next article.

Top comments (0)