Array — fixed size, value type. Slice — dynamic, reference type backed by an array.
Arrays in Go have a fixed length that is part of their type, meaning [3]int and [5]int are completely different types. Arrays are value types — assigning one array to another copies all elements. This makes them useful when you know the exact size at compile time, like for fixed-size lookups or when the data must not change size.
1// Array: fixed length, value type2var arr [5]int3arr[0] = 14arr2 := arr // copies all elements5arr2[0] = 99 // arr[0] is still 16fmt.Println(arr) // [1 0 0 0 0]
Slices are the workhorse collection in Go. A slice is a descriptor (pointer, length, capacity) pointing to an underlying array. Passing a slice to a function shares the underlying array — mutations are visible to the caller.
1// Slice: dynamic, reference semantics2s := []int{1, 2, 3}3s = append(s, 4) // may allocate new backing array4fmt.Println(s) // [1 2 3 4]56// Slicing shares memory7arr := [5]int{10, 20, 30, 40, 50}8sub := arr[1:3] // [20, 30] — points into arr9sub[0] = 200 // mutates arr[1] too10fmt.Println(arr[1]) // 200
When to use:
Common mistakes:
append without capturing the result — append(s, elem) returns a new slice header.make([]int, 10) and expecting capacity 10 — use the 3-arg form make([]int, 0, 10) if you want preallocated capacity.== — only valid against nil; use slices.Equal for content comparison.