Go Slices

A slice is Go's flexible, dynamically-sized view over an underlying array, and it is the collection type you will reach for almost every day.

What Is a Slice

A slice is a lightweight struct containing a pointer to an underlying array, a length, and a capacity. Unlike an array, a slice's type does not encode a size — []int can hold zero elements or a million. Slices are created with a slice literal, by slicing an existing array or slice with the [low:high] syntax, or with the built-in make function.

Creating Slices

package main

import "fmt"

func main() {
	fruits := []string{"apple", "banana", "cherry"}
	fmt.Println(fruits)

	numbers := make([]int, 3, 5)
	numbers[0] = 10
	fmt.Println(numbers, "len:", len(numbers), "cap:", cap(numbers))

	sub := fruits[1:3]
	fmt.Println(sub)
}

Growing Slices with append

The append function adds one or more elements to the end of a slice and returns the resulting slice, which you must assign back to the original variable. When a slice's length would exceed its capacity, Go allocates a new, larger underlying array (typically doubling it) and copies the existing elements over automatically — you never manage this memory by hand.

Appending Elements

package main

import "fmt"

func main() {
	var tasks []string
	tasks = append(tasks, "write code")
	tasks = append(tasks, "test code", "deploy code")

	fmt.Println(tasks)
	fmt.Println("len:", len(tasks), "cap:", cap(tasks))
}

len() vs cap()

len() reports how many elements a slice currently holds, while cap() reports how many elements the underlying array can hold before a reallocation is needed. Keeping an eye on capacity matters in performance-sensitive code: pre-sizing a slice with make([]T, 0, expectedSize) avoids repeated reallocations when you know roughly how many elements you'll append.

Inspecting len and cap

package main

import "fmt"

func main() {
	data := make([]int, 0, 4)
	for i := 1; i <= 6; i++ {
		data = append(data, i*i)
		fmt.Printf("len=%d cap=%d data=%v\n", len(data), cap(data), data)
	}
}
  • Declared with []Type — no length in the type, unlike arrays
  • make([]Type, length, capacity) pre-allocates underlying storage
  • append(slice, items...) grows a slice, reallocating when capacity is exceeded
  • Slicing an array or slice with [low:high] shares the same underlying data
Note: Because slices share an underlying array, modifying an element through one slice can be visible through another slice that overlaps the same array. Use copy() to make an independent duplicate when that sharing is unwanted.
OperationSyntaxResult
Create empty slicevar s []intnil slice, len 0, cap 0
Create with makemake([]int, 3, 10)len 3, cap 10
Appends = append(s, 4)new element added, may reallocate
Slice a slices[1:3]view sharing the same array
Note: A nil slice (var s []int) behaves like an empty slice for len(), cap(), and append() — but it is not the same as an empty slice literal []int{} when compared to nil directly.

Exercise: Go Slices

What is a Go slice, in terms of its relationship to an underlying array?