What is the difference between a nil slice and an empty slice? When does it matter?
Question 45MediumGo 1.22 to 1.25
var s []int is nil: ptr is nil, len and cap are 0. s := []int{} or make([]int, 0) is empty but non-nil: ptr points at a zero-size allocation (often runtime.zerobase). For almost everything they act the same: len, cap, range, append, and slicing s[0:0] all work on nil.
They differ in these places:
var a []int
b := []int{}
fmt.Println(a == nil, b == nil) // true false
j1, _ := json.Marshal(a) // null
j2, _ := json.Marshal(b) // []
fmt.Println(string(j1), string(j2))
fmt.Println(reflect.DeepEqual(a, b)) // false
fmt.Println(slices.Equal(a, b)) // true (length-based)
- JSON/API contracts:
nulland[]can break frontend clients. That is a common production bug. - Tests:
reflect.DeepEqualandtestify'sEqualtell them apart. - Interfaces: a nil
[]intstored inanyis a non-nil interface.
Idiom: prefer var s []T (the Go Code Review Comments guideline). Check emptiness with len(s) == 0, never s == nil. Initialize to []T{} only when the encoded form matters.
More on Arrays, Slices, Maps & Strings
- Q43What does this print? (two appends on the same base slice)
- Q44What is the full slice expression a[low:high:max], and when would you use it?
- Q46Explain the semantics of the built-in copy. Is it safe with overlapping slices?
- Q47How do arrays differ from slices? Can you convert between them?
- Q48What does this print? (range over an array vs a slice)
- Q49How can subslicing cause memory leaks, and how do you fix them?