“This is the 16th day of my participation in the First Challenge 2022. For details: First Challenge 2022”

Hello, everyone. I’m a fisherman from Go School.

We know that slice segmentation is actually an operation on slice’s underlying array. Slicing an existing slice creates a new slice that points to the same underlying array. Therefore, if an index value is visible to both slices, updating one slice with the index (for example, s1[1] = 10) also affects the other slice.

This article describes a common error when using Append, which can cause side effects in some scenarios.

First, we have the following example: initialize a slice s1, then create a slice S2 by shard s1, and then create a slice S3 by append on S2:

    s1 := []int{1.2.3}
    s2 := s1[1:2]
    s3: = append(s3, 10)
Copy the code

As you can see from the above code, S1 contains three elements. Shard S1 to create S2. Then append s2 to create S3. So, what are the states of the last three slices?

The following is an example of the state of S1 and S2 in memory:

S1 is a slice structure with length of 3 and capacity of 3, while S2 is a slice structure with length of 1 and capacity of 2. Both s1 and S2 point to the same underlying array.

When append is used to add elements to a slice, it checks whether the slice is full: The element is full when the length of the slice is equal to the size of the slice. If not, the append function adds the element to the free space of the original underlying data and returns a new structure.

In this example, S2 is not full and can still receive one element. Therefore, the following figure is the final state of the three slices, as shown below:

As can be seen from the figure, three slices share an underlying data, and the last element of the data is updated as 10. So, if we print these three slices, we get the following output:

s1=[1 2 10], s2=[2], s3=[2 10]
Copy the code

It can be seen that the contents of S1 are modified even though s1[2] is not modified and s1[1] is not modified. Therefore, we should keep this rule in mind to avoid accidental errors.

Let’s look at another effect: the effect when the new slice obtained by shard is passed as a function parameter.

Let’s look at the following example code:

func main(a) {
    s := []int{1.2.3}
	
    f(s[:2])
    // Use s
}

func f(s []int) {
    // Update s
}
Copy the code

This implementation is very dangerous. In fact, the function f has a side effect on slicing the input. For example, if function f calls append(s, 10), the content of s in main will no longer be [1 2 3], but [1 2 10].

How can we solve these problems?

We can copy the original slice and then build a new slice variable, as shown in the following code:

func main(a) {
    s := []int{1.2.3}
    sCopy := make([]int.2)
    copyI'm really sorry I'm doing that for you. (YOU know I'm doing that for youappend(sCopy, s[2) 2.// Use result
}

func f(s []int) {
    // Update s
}
Copy the code

① Copy the first two elements of S into sCopy

② Build a new result slice by adding S [2] to the sCopy by append function

Because we passed a copy in function f, there is no side effect to the slice even if append is called in the function. The disadvantage of this scheme is that the existing slices need to be copied once. If the slices are very large, storage and performance will become problems during copying.

Scheme 2: Limit slice capacity This scheme avoids the side effects of the original slice by limiting the slice capacity and automatically generating a new underlying data during the operation of the slice. This scheme is known as the full slice expression: s[low:high: Max]. The difference between this full slice expression and S [low:high] is that the capacity of the slice of S [low:high: Max] is max-low, while the capacity of S [low:high] is the maximum capacity of the underlying data in S minus LOW.

func main(a) {
    s := []int{1.2.3}
    f(s[:2:2) 1.// Use s
}

func f(s []int) {
    // Update s
}
Copy the code

① Pass a subslice using a full slice expression

The slice passed to the f function in the above code is not S [:2], but S [:2:2]. Therefore, the capacity of slices is 2-0 = 2, as shown below:

This solution shares the underlying array of slices while avoiding side effects by limiting capacity.

We must always be aware of the possibility of data side effects between slices when cutting from one slice into subslices. This can happen when you modify an element directly or when you use the append function. If we want to solve this side effect, we can solve it by using a full slice expression. This approach avoids extra copies and is relatively efficient.