💻 Tech
Since a slice has a backing array, a slice of a slice operates on the same backing array. Since slices are using pointer semantics, one must be care not to cause unintended side effects.
In the output below, you can see that the slice of slice (2nd and 3rd index of the original slice) share the same backing array by looking at the memory addresses of its elements.
A potential side effect when modifying a slice derived from another slice is mutation of the backing array. In the output below, you can see that when location 0xc0000ae020 was modified, it can be seen across the two slices, original and derived.
One way to remediate that is by using copy command (see Demo program 2)
Demo program 1
func main() {
fmt.Println("#####")
fmt.Println("original slice")
greetings := []string{"ola", "nihao", "salut", "oi", "kamusta"}
displaySlice(greetings)
fmt.Println("\n#####")
fmt.Println("slice of slice")
greetings1 := greetings[2:4] // 2nd and 3rd index
displaySlice(greetings1)
fmt.Println("\n#####")
fmt.Println("side effect: modified new slice index")
greetings1[0] = "MUDOU"
fmt.Println("original slice")
displaySlice(greetings)
fmt.Println("slice of slice")
displaySlice(greetings1)
}
func displaySlice(s []string) {
fmt.Printf("len:%v, cap:%v\n", len(s), cap(s))
for i, v := range s {
fmt.Printf("%v %v %v\n", i, &s[i], v)
}
}
Demo program 1 output
#####
original slice
len:5, cap:5
0 0xc0000ae000 ola
1 0xc0000ae010 nihao
2 0xc0000ae020 salut
3 0xc0000ae030 oi
4 0xc0000ae040 kamusta
#####
slice of slice
len:2, cap:3
0 0xc0000ae020 salut # address as the original slice
1 0xc0000ae030 oi # address as the original slice
#####
side effect: modified new slice index
original slice
len:5, cap:5
0 0xc0000ae000 ola
1 0xc0000ae010 nihao
2 0xc0000ae020 MUDOU
3 0xc0000ae030 oi
4 0xc0000ae040 kamusta
slice of slice
len:2, cap:3
0 0xc0000ae020 MUDOU
1 0xc0000ae030 oi
In the modified program below, a new slice is safely copied from the original slice, ensuring that the new slice has its own backing array. This means any changes to the new slice will not affect the original slice since the new slice has its own backing array.
Demo program 2
func main() {
fmt.Println("#####")
fmt.Println("original slice")
greetings := []string{"ola", "nihao", "salut", "oi", "kamusta"}
displaySlice(greetings)
fmt.Println("\n#####")
fmt.Println("slice of slice")
greetings1 := make([]string, len(greetings)) // ensuring same slice length before copy
copy(greetings1, greetings) // copy items from original to new slice
displaySlice(greetings1)
fmt.Println("\n#####")
fmt.Println("only the new slice is affected by the change")
greetings1[0] = "MUDOU"
fmt.Println("original slice")
displaySlice(greetings)
fmt.Println("slice of slice")
displaySlice(greetings1)
}
func displaySlice(s []string) {
fmt.Printf("len:%v, cap:%v\n", len(s), cap(s))
for i, v := range s {
fmt.Printf("%v %v %v\n", i, &s[i], v)
}
}
```bash
#####
original slice
len:5, cap:5
0 0xc000100050 ola
1 0xc000100060 nihao
2 0xc000100070 salut
3 0xc000100080 oi
4 0xc000100090 kamusta
#####
slice of slice
len:5, cap:5
0 0xc0001000a0 ola
1 0xc0001000b0 nihao
2 0xc0001000c0 salut
3 0xc0001000d0 oi
4 0xc0001000e0 kamusta
#####
only the new slice is affected by the change
original slice
len:5, cap:5
0 0xc000100050 ola
1 0xc000100060 nihao
2 0xc000100070 salut
3 0xc000100080 oi
4 0xc000100090 kamusta
slice of slice
len:5, cap:5
0 0xc0001000a0 MUDOU
1 0xc0001000b0 nihao
2 0xc0001000c0 salut
3 0xc0001000d0 oi
4 0xc0001000e0 kamusta