A slice is empty when its length is zero. A nil slice is therefore empty, but an empty slice is not necessarily nil. Confusing those tests can make code process a slice that contains no elements. 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: the declaration and nil conversion produce nil slices; the literal and make call produce non-nil empty slices. section 3.6
Check for elements, not non-nilness
If getOperations returns make([]float32, 0), this condition still calls handle: ```go if operations != nil { handle(operations) } ``` Test the length instead. It works whether the returned empty slice is nil or non-nil: ```go if len(operations) != 0 { handle(operations) } ``` section 3.7
For a result assembled with append, start with `var s []string`: it needs no initial allocation, and append works on nil slices. If the result length is known, `make([]string, length)` lets you fill elements by index without growth-related allocations and copies. section 3.6
When nil and empty differ at a boundary
`encoding/json` encodes a nil slice field as `null` and a non-nil empty slice field as `[]`. Choose deliberately if that output matters, but do not use the distinction as the ordinary test for whether elements exist. section 3.6 section 3.7
Design slice-returning APIs so callers need not assign different meanings to nil and empty results. When consuming an API you cannot change, `len(s) == 0` remains the reliable empty check. section 3.7
