Go slice (comparatively) deeper insights (Episode 2)

In this episode, we will try to tackle the classic problem mentioned in Episode 0.

func myFunc(s []int) {
 s[0] = 10
 s = append(s, 6)
}

func main() {
 mySlice := []int{1, 2, 3, 4, 5}
 myFunc(mySlice)
 fmt.Println(mySlice)
}

Output : [10 2 3 4 5]        

Why does s[0] = 10 applies to mySlice while appending 6 does not?

I wrote a similar program with more strategic prints so that we can track the memory information about each slice.

On line 27, slice mySlice with initial values 1, 2, 3, 4, 5 is created.

From line 28-30, information about mySlice memory address, mySlice.Data memory address and mySlice's len and cap are printed. Note that instead of using make() function, we initialized a slice literal with initial values, which means the length and capacity of mySlice will always be the same.

On line 32, we call myFunc with mySlice as argument. myFunc is invoked.

From line 9-11, we print the same information of s as that of mySlice. As we can see, except memory location of s itself, other information is identical to that of mySlice. So it is fair to say a copy of the argument is created somewhere else in memory that contains exactly the same values for its fields.

On line 13, we change s[0] to 10. It is worth mentioning that the memory address of s[0] is not only used as s.Data, it is also being used as mySlice.Data, that is why mySlice[0] is updated simultaneously to 10.

From line 14-17, we again print the same information of s. As expected, nothing has changed: s is still in the same memory space, s.Data is pointing to the same address where mySlice.Data is pointing to, Len and Cap did not change either.

On line 19, we append 6 to s and give the evaluated result (a new slice at a different memory address with a new Len and Cap, see how it works in Episode 1) to s.

From line 20-22, we again print the same information of s. As we can see, s is still in the same memory space, however, s.Data, s.Len and s.Cap have changed. This behaviour has already been explained thoroughly in Episode 1. Now take a closer look, does the aforementioned changes affect mySlice? Not at all. Because all changes happened on that SliceHeader s stored at 0xc000180000, it has nothing to do with mySlice stored at 0xc000010018. Also as explained in Episode 1, even a new underlying array is created, the original array still stays intact AND myslice.Data is still pointing to the 0th element of that array.

On line 33, mySlice is printed and as explained above, its value is now 10, 2, 3, 4, 5

From line 35-37, same information of mySlice is printed again and they stay the same.

To sum up, the reason why s[0] = 10 takes effect is that when we change s[0] to 10, s[0] and mySlice[0] happen to be at the same memory location. The reason why appending 6 does not take effect is that a new underlying array is created somewhere else in memory by copying the original array's values and adding 6 at the very end. However, the original array is by no means mutated.

I drew a diagram explaining the whole process.

Try to switch line 13 and 19, guess what mySlice will end up being. The answer is mySlice will not be mutated because once 6 is being appended, a new underlying array is already created, s.Data is pointing to that new memory address and all changes afterwards happens on new array.

In this episode, we covered passing slice as value. We will cover passing slice as reference in Episode 3.


要查看或添加评论,请登录

Wenzhen Gong的更多文章

社区洞察

其他会员也浏览了