TL;DR: The slice length is the number of available elements in the slice, whereas the slice capacity is the number of elements in the backing array, counting from the first element in the slice.
It’s common for Go developers to mix slice length and capacity or not understand them thoroughly. Assimilating these two concepts is essential for efficiently handling core operations such as slice initialization and adding elements with append, copying, or slicing. This misunderstanding can lead to using slices suboptimally or even to memory leaks.
In Go, a slice is backed by an array. That means the slice’s data is stored contiguously in an array data structure. A slice also handles the logic of adding an element if the backing array is full or shrinking the backing array if it’s almost empty.
Internally, a slice holds a pointer to the backing array plus a length and a capacity. The length is the number of elements the slice contains, whereas the capacity is the number of elements in the backing array, counting from the first element in the slice. Let’s go through a few examples to make things more transparent. First, let’s initialize a slice with a given length and capacity:
s := make(int, 3, 6) // Three-length, six-capacity slice
The first argument, representing the length, is mandatory. However, the second argument representing the capacity is optional. Figure 1 shows the result of this code in memory.
In this case,
make creates an array of six elements (the capacity). But because the length was set to 3, Go initializes only the first three elements. Also, because the slice is an
int type, the first three elements are initialized to the zeroed value of an
int: 0. The grayed elements are allocated but not yet used.