Nil slice — zero value of []T, no backing array, pointer/length/capacity all zero. Empty slice — has a backing array but no elements; len == 0, cap may be > 0.
Both support len, cap, range, and append identically. The difference is in identity checks and serialization.
1var nilS []int // nil2emptyS := []int{} // non-nil, empty3madeS := make([]int, 0, 10) // non-nil, empty, cap=1045fmt.Println(nilS == nil) // true6fmt.Println(emptyS == nil) // false7fmt.Println(madeS == nil) // false89fmt.Println(len(nilS), cap(nilS)) // 0 010fmt.Println(len(emptyS), cap(emptyS)) // 0 011fmt.Println(len(madeS), cap(madeS)) // 0 10
JSON behavior:
1n, _ := json.Marshal(nilS) // "null"2e, _ := json.Marshal(emptyS) // "[]"
When to use:
[] not null.Common mistakes:
null.len == 0 when nil vs empty matters.append(nilSlice, ...) panics — it's safe.slices.Equal(nilS, emptyS) returns false even though both are empty.