thecodex.expert · The Codex Family of Knowledge
Go

Slices and Arrays

Arrays exist in Go but are rarely used directly — almost all real code reaches for slices, which can grow and shrink.

Go 1.26 len vs cap Last verified:
Canonical Definition

An array’s length is part of its type ([5]int and [10]int are different types), which makes arrays inflexible and rarely used directly. A slice is a small struct — a pointer to an underlying array, a length, and a capacity — that provides a flexible, growable view over that array, and is what idiomatic Go code uses for lists in practice. len(s) reports how many elements a slice currently holds; cap(s) reports how many it could hold before its backing array must be reallocated.

Arrays: fixed size, part of the type

An array's length is fixed at compile time and is part of its type. This makes arrays value types — assigning or passing one copies every element — which is exactly why Go code almost always uses slices instead.

Goarrays.go
var scores [3]int          // array of exactly 3 ints, zero-valued
scores[0] = 90

primes := [5]int{2, 3, 5, 7, 11}
fmt.Println(len(primes))   // 5 — fixed forever for this variable

Slices: a view over an array

A slice is created with []T{...} literal syntax, make([]T, len, cap), or by slicing an existing array/slice with a[low:high]. Two slices can share the same underlying array — mutating one through indexing can be visible through the other, since neither owns a private copy.

Goslices.go
nums := []int{10, 20, 30, 40, 50}   // slice literal
sub := nums[1:3]                     // [20, 30] — shares nums' backing array

sub[0] = 999
fmt.Println(nums)   // [10, 999, 30, 40, 50] — nums changed too!

append() and when a slice grows

append adds elements to a slice. If the slice still has spare capacity, append writes into the existing backing array and returns a slice pointing at the same array. If capacity is exhausted, Go allocates a new, larger backing array (typically doubling for smaller slices), copies the old data over, and returns a slice pointing at the new array — which is why append's result must always be reassigned, since the original variable's backing array may no longer be the current one.

Goappend.go
s := make([]int, 0, 2)   // len 0, cap 2
s = append(s, 1)          // fits: len 1, cap 2, same backing array
s = append(s, 2)          // fits: len 2, cap 2, same backing array
s = append(s, 3)          // doesn't fit: Go allocates a new, bigger array

// always reassign: s := append(s, x) NOT append(s, x) alone

Sources

1
The Go Authors. "Go Slices: usage and internals", go.dev/blog/slices-intro — the canonical explanation of slice growth and backing arrays.