💡 Empty slice vs nil slice in Go
Slices in Go can be represented by 3 elements:
ptr- a pointer to the underlying array that contains data of the slicelen- length, number of elements in the slicecap- capacity, number of elements in the underlying data array, starting from the element pointed by theptr
A nil slice declared as var s1 []string has no underlying data array - it points to nothing. An empty slice, declared as s2 := []string{} or s3 := make([]string, 0) points to an empty, non-nil array.
See also in detail what is the difference between length and capacity of slices
See the table to compare the properties of the nil and empty slices.
| ptr | len | cap | |
|---|---|---|---|
nil: []string | 0 | 0 | 0 |
empty: []string{} | <addr> | 0 | 0 |
empty: make([]string, 0) | <addr> | 0 | 0 |
The <addr> is a non-zero address to an empty, non-nil array.
package main
import "fmt"
func main() {
var s1 []string // nil
s2 := []string{} // empty
s3 := make([]string, 0) // empty, equivalent to s2 := []string{}
fmt.Printf("s1 is nil: %t, len: %d, cap: %d\n", s1 == nil, len(s1), cap(s1))
fmt.Printf("s2 is nil: %t, len: %d, cap: %d\n", s2 == nil, len(s2), cap(s2))
fmt.Printf("s3 is nil: %t, len: %d, cap: %d\n", s3 == nil, len(s3), cap(s3))
}
Output:
s1 is nil: true, len: 0, cap: 0
s2 is nil: false, len: 0, cap: 0
s3 is nil: false, len: 0, cap: 0
Empty and nil slices behave in the same way that is the built-in functions like len(), cap(), append(), and for .. range loop return the same results. So, since the nil slice declaration is simpler, you should prefer it to creating an empty slice. However, there are some cases when you may need the empty, non-nil slice. For instance, when you want to return an empty JSON array [] as an HTTP response, you should create the empty slice ([]string{} or make([]string, 0)) because if you use a nil slice, you will get a null JSON array after encoding:
package main
import (
"encoding/json"
"fmt"
"log"
)
func main() {
var s1 []string // nil
s2 := []string{} // empty
s3 := make([]string, 0) // empty, equivalent to s2 := []string{}
s1JSON, err := json.Marshal(s1)
if err != nil {
log.Fatal(err)
}
fmt.Printf("s1 JSON: %s\n", string(s1JSON))
s2JSON, err := json.Marshal(s2)
if err != nil {
log.Fatal(err)
}
fmt.Printf("s2 JSON: %s\n", string(s2JSON))
s3JSON, err := json.Marshal(s3)
if err != nil {
log.Fatal(err)
}
fmt.Printf("s3 JSON: %s\n", string(s3JSON))
}
Output:
s1 JSON: null
s2 JSON: []
s3 JSON: []