Slice in Go is, colloquially, a dynamic array. We all know that the length of an array is fixed, once declared, the length of an array is fixed; However, the slice length is not fixed, and you can still increase the slice length after the declaration. Let’s first look at the data structure of slice.

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

As shown above:

  • Array is a pointer to an array
  • Len is the number of elements in the current slice
  • Cap represents the capacity of the current slice (i.e., the maximum number of elements it can hold)

Where an array points is a contiguous memory space that stores sliced elements; The contiguous memory space + length + capacity forms a slice, which can be regarded as an abstraction of array, because if Len and Cap remain unchanged, slice is essentially an array. But slice and cap change, so that’s slicing.

Section declaration:

m := []int{1, 2, 3}
m := make([]int, 3)
Copy the code

If we want to get a slice, we can do this

M: int [] = {1, 2, 3} n0: = m [2-0] / / output n1: [1, 2] = m [2] / / output [1, 2]Copy the code

Obtain a part of the slice by means of m[start:end], including start but not including end, or not writing start. M [:2] means from the beginning to the element with subscript 1 in the original slice, without writing end, m[1:] means from the element with subscript 1 to the end of the original slice element. Let’s look at one more interesting piece of code to understand slice:

func main() {
    m := []int{1, 2, 3}  
    n := m[:2]  n = append(n, 4)  
    n = append(n, 5)  
    n = append(n, 6)  
    fmt.Printf("%v \n", m)  
    n[0] = 7  
    fmt.Printf("%v", m)
}
Copy the code

So what is the output of the above code? Do you initially think that the first output is: [1,2,4,5,6] and the second output is: [7,2,4,5,6]

The correct answer is definitely not. The correct output here is both: [1,2,4]

why? Let’s analyze this code line by line

  • M := []int{1, 2, 3}, len of m is 3, cap is 3
  • N := m[:2], n becomes [1,2], m is still [1,2,3], n references part of m, len of n is 2, cap is 3.
  • N = append(n, 4), n becomes [1,2,4], m becomes [1,2,4], since n and m refer to the same contiguous space, n modifies the third element, and m also changes.
  • N = append(n, 5), the capacity of n is 3, at this time, it is found that the capacity is not enough, need to expand, expand twice, cap becomes 6, after the expansion opens up a new continuous memory space, at this point, n and M have no relationship, so no matter how n append elements or change elements, it has no relationship with M.

So both outputs are [1,2,4]. Here we learned about the slice expansion mechanism: when cap is insufficient, the slice is doubled if the number of elements is less than 1024; If the number of elements is greater than 1024, it is enlarged by 1.25 times and becomes a new slice after each expansion.