What does comparing a nil slice and an empty slice print?
Determine what this prints, then state the practical difference between a nil slice and an empty non-nil slice — including len, cap, append/range behaviour, and how each marshals to JSON.
var a []int // nil slice
b := []int{} // empty, non-nil slice
fmt.Println(a == nil, b == nil) // ?
fmt.Println(len(a), cap(a), len(b)) // ?
Predict the output.
A nil slice (var a []int) has no backing array and a == nil is true; an empty slice ([]int{}) is non-nil with len 0. Both have len 0, accept append, and range cleanly. Prefer nil for "no results" — but a nil slice marshals to JSON null while an empty one marshals to [].
- ✗Believing you cannot
appendto anilslice —appendallocates a backing array on first growth - ✗Assuming
[]int{}compares equal tonil— it is non-nil - ✗Overlooking that a
nilslice marshals to JSONnull, not[]
- →Why does
appendwork identically on anilslice and an empty one despite the== nildifference? - →When does the
nullvs[]JSON distinction actually break an API contract?
What does this code print?
var a []int // nil slice
b := []int{} // empty, non-nil slice
fmt.Println(a == nil, b == nil) // ?
fmt.Println(len(a), cap(a), len(b)) // ?
Output
true false
0 0 0
The difference
nil slice (var a []int) | empty slice ([]int{}) | |
|---|---|---|
== nil | true | false |
| backing array | none | allocated (length 0) |
len / cap | 0 / 0 | 0 / 0 |
append | works | works |
range | 0 iterations | 0 iterations |
| JSON marshal | null | [] |
For reading, append, and range, the two slices are indistinguishable — so it is idiomatic to return a nil slice for "no results" instead of an extra []int{} allocation.
⚠️ The one place the difference matters is serialization: json.Marshal(nil slice) produces null, while an empty slice produces []. If an API client expects an array, return []int{} or normalize at the boundary.