A nil slice equals `nil` and has length zero. An empty slice also has length zero, but it need not be nil. Confusing the two can turn a check for elements into a check for how the slice was initialized. section 3.6 section 3.7
func main() {
var s []string #1
log(1, s)
s = []string(nil) #2
log(2, s)
s = []string{} #3
log(3, s)
s = make([]string, 0) #4
log(4, s)
}
func log(i int, s []string) {
fmt.Printf("%d: empty=%t\tnil=%t\n", i, len(s) == 0, s == nil)
}
Compare four zero-length initializations: `var s []string` and `[]string(nil)` are nil; `[]string{}` and `make([]string, 0)` are not. section 3.6
Prefer `var s []string` when a function might return no elements: a nil slice needs no allocation, and `append` still works on it. If the result length is known, use `make([]string, length)` instead to avoid extra allocations and copies. section 3.6
A presence check that handles both forms
Suppose `getOperations` returns `make([]float32, 0)` when there are no operations. `if operations != nil { handle(operations) }` still calls `handle`. Use `if len(operations) != 0 { handle(operations) }`: it excludes both nil and non-nil empty slices without requiring a change to `getOperations`. APIs should not give nil and empty slices different meanings for callers. section 3.7
When the distinction is observable
`encoding/json` marshals a nil slice field as `null` and a non-nil empty slice field as `[]`. Check that output when the JSON representation matters, even though both slices contain no elements. section 3.6
A nil slice can also be an `append` destination: `dst := append([]int(nil), src...)` copies the elements of `src`. When using `copy` instead, give the destination enough length; copying into `var dst []int` copies no elements. section 3.8
