Learn Go← Dashboard

Fixed-size arrays and the workhorse slice type.

Slices: make, append, copy

A slice is a flexible, dynamically-sized view into an array. It's the type you'll use for "a list of things" 99% of the time.

A slice is internally a tiny three-field struct: a pointer to the underlying array, a length, and a capacity.

Creating slices

nums := []int{1, 2, 3}        // composite literal
empty := []int{}              // empty (len=0, cap=0)
zeros := make([]int, 5)       // [0 0 0 0 0]
buffer := make([]int, 0, 10)  // len=0, cap=10 — room for 10 without realloc

Note the missing length in the type — []int is a slice; [3]int is an array.

append

append returns a new slice, possibly with a new backing array if there wasn't enough capacity.

go playground
Loading...

Slicing operator s[low:high]

s := []int{0, 1, 2, 3, 4}
mid := s[1:4]   // [1 2 3]   — high is exclusive
head := s[:2]   // [0 1]
tail := s[3:]   // [3 4]
all  := s[:]    // [0 1 2 3 4]

A slice of a slice shares the same underlying array. Mutating one can be visible to the other:

go playground
Loading...

copy — make an independent slice

dst := make([]int, len(src))
n := copy(dst, src)   // n == min(len(dst), len(src))

copy is how you produce an independent slice (no shared backing array).

Removing an element

There's no remove, but the idiom is short:

s = append(s[:i], s[i+1:]...)         // preserves order
s[i] = s[len(s)-1]; s = s[:len(s)-1]  // doesn't preserve order, but O(1)

Since Go 1.21, the slices package in the standard library gives you helpers like slices.Delete, slices.Insert, slices.Contains, and slices.Sort:

import "slices"
s = slices.Delete(s, i, i+1)

Passing slices to functions

When you pass a slice to a function, you pass a copy of the slice header (pointer + len + cap) but you both see the same underlying array. So changes to elements are visible to the caller:

func zero(s []int) {
    for i := range s { s[i] = 0 }
}

xs := []int{1, 2, 3}
zero(xs)
fmt.Println(xs) // [0 0 0]

But append inside a function may reallocate, in which case the caller's slice is unchanged. Return the new slice from the function and reassign — same rule as inside one function.

`a := []int{1, 2, 3, 4, 5}; b := a[1:4]; b[0] = 99` — what is `a` after this?