liveBook · Manning

Capacity

As explained in 100 Go Mistakes and How to Avoid Them

Slice capacity is the number of elements in the underlying array available to a Go slice from its starting position.

A common mistake is treating a slice’s capacity as the number of elements you can index. Length counts the elements currently in the slice; capacity counts the backing-array space available from the slice’s starting position. A slice holds a reference to that array along with its length and capacity. Space beyond the length is allocated but cannot be indexed until the slice grows. section 3.4

Allocated does not mean indexable

s := make([]int, 3, 6) s[1] = 1 // valid: length remains 3 // s[4] = 0 // panic: index out of range s = append(s, 2) // uses available capacity; length becomes 4 section 3.4

Slicing does not necessarily allocate a new array. Two slices can start at different positions in the same backing array, so they can have different lengths and capacities while sharing elements. Changes to a shared element are visible through both slices. An append that fits within available capacity can also write to their shared array; once that array is full, appending creates another array and copies the elements. section 3.4

Figure 3.6 The slices s1 and s2 reference the same backing array with different lengths and capacities.

The diagram locates two slices in one backing array, making their different starting positions, lengths, and capacities visible. section 3.4

Choose the allocation strategy to match how the result will be filled:

  • If you will append a known number of elements, use make([]Bar, 0, n). Reserving capacity avoids repeatedly allocating backing arrays as the slice grows. section 3.5
  • If you will fill positions by index, use make([]Bar, n) and assign bars[i]. Those n positions already belong to the slice; appending instead would add elements after them. section 3.5

Avoid repeated growth

func convert(foos []Foo) []Bar { bars := make([]Bar, 0, len(foos)) for _, foo := range foos { bars = append(bars, fooToBar(foo)) } return bars } Starting with an empty slice without reserved capacity makes this conversion allocate and copy backing arrays as it grows. Here, the output size is known, so one capacity reservation fits the append-based implementation. section 3.5

Capacity also matters when a slice is shortened. Returning foos[:2] from a large slice retains its backing array. If excluded elements contain pointers or slice fields, their referenced data can remain in memory even though those elements are outside the new length. Copy the wanted elements into a new slice to release the old backing array, or clear the excluded pointer-bearing fields before returning a slice of the original array. Use a copy when the result must not retain the old array; clear fields when sharing that array is acceptable. section 3.10.2 section 3.14

How 100 Go Mistakes and How to Avoid Them covers it

How deep it goes
Defines capacity against length, traces shared backing arrays and growth, and works through preallocation and retention fixes without runtime implementation details.
Examples
Shows a length-three, capacity-six slice, a preallocated conversion function, and a shortened slice retaining pointer-bearing elements.
What it assumes
Assumes basic Go syntax while defining slice length, capacity, and backing-array behavior through make, append, and slicing.
What you can do after
Diagnose/debug with it to distinguish invalid indexing, shared-array append effects, repeated allocations, and memory retained after slicing.
How it connects
Connects slice length and backing arrays to append, slicing, copying, preallocation, and garbage collection.

Also explained in

Compare how 5 books explain capacity →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of 100 Go Mistakes and How to Avoid Them. The section links point to the parts of the book it draws on.