Slices
Prerequisites: Arrays
A slice is a flexible, growable view of a sequence of values —
the list type you'll use most in Go. Its type is written without
a length: []int, []string.
Under the hood, a slice is a small descriptor with three parts: a pointer to an underlying (backing) array, a length (how many elements it currently holds), and a capacity (how many fit before a bigger backing array is needed).
Syntax
nums := []int{1, 2, 3} // slice literal
buf := make([]int, 3, 10) // length 3, capacity 10
var empty []int // nil slice: length 0, ready for append
nums = append(nums, 4, 5) // append returns the (possibly new) slice
len(nums) // number of elements
cap(nums) // capacity
nums[1:3] // elements at index 1 and 2
nums[:2] // from the start up to (not including) 2
nums[2:] // from index 2 to the end
Example
package main
import "fmt"
func main() {
nums := []int{1, 2, 3}
nums = append(nums, 4, 5)
fmt.Println(nums, len(nums))
buf := make([]int, 3, 10)
fmt.Println(buf, len(buf), cap(buf))
var names []string
fmt.Println(names == nil, len(names))
names = append(names, "Ada", "Alan")
fmt.Println(names)
part := nums[1:3]
fmt.Println(part, len(part))
part[0] = 99 // changes the shared backing array
fmt.Println(nums)
independent := make([]int, len(part))
copy(independent, part)
independent[0] = 0
fmt.Println(part, independent)
}
Expected output
[1 2 3 4 5] 5
[0 0 0] 3 10
true 0
[Ada Alan]
[2 3] 2
[1 99 3 4 5]
[99 3] [0 3]
The important line is [1 99 3 4 5]: slicing does not copy.
part and nums share the same backing array, so changing
part[0] changed nums[1] too. When you need an independent
copy, use make plus the built-in copy, as shown.
When append runs out of capacity, it allocates a new, larger
backing array and copies the elements over — that's why you must
always use the slice append returns. How much the capacity
grows is an implementation detail, so don't rely on exact cap
values after appending.
The Go 1.21+ slices package adds helpers such as
slices.Contains, slices.Index, and slices.Sort.
Common mistakes
- Calling
appendwithout keeping the result.append(nums, 4)on its own is a compile error (append(nums, 4) (value of type []int) is not used); writenums = append(nums, 4). - Forgetting that sub-slices share memory with the original, then being surprised when a change "leaks" between them.
- Indexing past
len—nums[10]on a 5-element slice panics withindex out of range [10] with length 5, even if the capacity is larger. - Confusing
make([]int, 5)(five zeros already inside) withmake([]int, 0, 5)(empty, room for five). Appending to the first gives six elements.
Practice
Starting from an empty slice, use a loop and append to build a
slice of the squares of 1 to 5. Print the whole slice, its
length, and then only the last three squares using a slicing
expression.
Show solution
package main
import "fmt"
func main() {
var squares []int
for i := 1; i <= 5; i++ {
squares = append(squares, i*i)
}
fmt.Println(squares) // [1 4 9 16 25]
fmt.Println(len(squares)) // 5
fmt.Println(squares[2:]) // [9 16 25]
}