Dev Skills

Arrays vs slices in Go: the differences, with examples

An array is a fixed box of values. A slice is a window onto one. You will use slices 99% of the time.

Topic 90 of 98 · 5 min to read · 20 min to build
After this topic you can
  • List the differences between an array and a slice in Go.
  • Predict when changing one slice changes another.
  • Say when an array is the better choice.

An array has a fixed length that is part of its type: [3]int. A slice is a small header (pointer, length, capacity) that looks at part of an array: []int. Here is the difference at a glance.

Array `[3]int`Slice `[]int`
LengthFixed, part of the typeCan grow with append
Passed to a functionWhole array is copiedOnly the header is copied; data is shared
Zero valueAll elements zeronil (length 0)
Compare with ==YesNo (only with nil)
Use as a map keyYesNo
len / capBoth equal the fixed lengthCan differ
How often you use itRarelyAll the time

What is the difference between an array and a slice in Go?

An array’s length is fixed and part of its type, and assigning or passing it copies every element. A slice is a view over an array that can grow, and copying a slice shares the same elements. Full details are in Arrays and slices.

copy-vs-share.go
package main
import "fmt"
func setFirstArray(a [3]int) { a[0] = 99 } // gets a copy
func setFirstSlice(s []int) { s[0] = 99 } // shares the data
func main() {
arr := [3]int{1, 2, 3}
sl := []int{1, 2, 3}
setFirstArray(arr)
setFirstSlice(sl)
fmt.Println("array after call:", arr) // unchanged
fmt.Println("slice after call:", sl) // changed
b := arr // copies the array
b[1] = 0
fmt.Println("arr == [3]int{1, 2, 3}:", arr == [3]int{1, 2, 3})
t := sl // copies only the header
t[1] = 0
fmt.Println("sl after t[1] = 0:", sl)
}

Is a slice passed by reference in Go?

No. Go passes everything by value, slices too. But a slice value is a header holding a pointer, so the copy points at the same elements. Changing s[i] is seen by the caller; append that grows the slice is not, unless you return it.

What are len and cap of a slice?

len is how many elements you can index now. cap is how many fit in the backing array before append must allocate a new, bigger one. When that happens the slice moves to new memory and stops sharing with the old one.

len-cap.go
package main
import "fmt"
func main() {
a := []int{1, 2, 3, 4}
b := a[:2]
fmt.Println(len(b), cap(b)) // 2 4: room left in a's array
b = append(b, 9) // fits, so it writes into a's array
fmt.Println("a:", a)
c := a[:2:2] // capacity capped at 2
c = append(c, 7) // no room: copies to a new array
fmt.Println("a:", a, "c:", c)
}
The first append overwrote a[2]. The capped one did not. This is the trap interviewers love.

When should you use an array instead of a slice?

When the size is fixed and meaningful, and you want copying or comparison: a SHA-256 hash ([32]byte), an RGB colour ([3]uint8), or a map key like [2]int for grid coordinates. Everywhere else, use a slice.

The short version
  • Array: fixed length, part of the type, copied on assign and pass.
  • Slice: pointer + len + cap over an array; copies share elements.
  • Arrays can be compared and used as map keys; slices cannot.
  • append may move a slice to a new array; always write s = append(s, x).
  • Use s[a:b:b] to stop append overwriting the original.
Remember: Arrays are values with a fixed size. Slices are views that share memory. Use slices unless the size is truly fixed.
Then try these in your own editor
  1. 1What does fmt.Println([2]int{1, 2} == [2]int{1, 2}) print? What about the same with slices?
  2. 2Write a function that appends to a slice and make the caller see the new element.
  3. 3Use map[[2]int]bool to mark visited cells (0,0) and (1,2) in a grid, then print whether (1,2) was visited.