In Go, there is a type called slice which is built on top of array. It's a very convenient type when we want to handle a group of data. This post will explain a subtle but tricky difference between empty slice and nil slice.
A nil slice is a slice has a length and capacity of zero and has no underlying array. The zero value of slice is nil. If a slice is declared like below, it is a nil slice.
package main
import "fmt"
func main() {
var a []string
fmt.Println(a == nil)
}
The output will be true for above code snippet.
An empty slice is a slice also has a length and capacity of zero but has underlying array with length zero. If a slice is declared like below, it is an empty slice.
package main
import "fmt"
func main() {
b := []string{}
fmt.Println(b == nil)
}
The output will be false for above code snippet.
An empty slice can also be created with the make() function.
package main
import "fmt"
func main() {
c := make([]string, 0)
fmt.Println(c == nil)
}
In most cases, empty slice and nil slice can be treated the same. Only in some subtle cases, they should be treated differently, one of them is when doing JSON encoding.
An empty slice will be encoded as an [] in JSON while nil slice will be encoded as null.
package main
import (
"fmt"
"encoding/json"
)
type A struct {
Data []string
}
func main() {
var a []string
fmt.Println(a == nil)
as := &A{
Data: a,
}
aj, _ := json.Marshal(as)
fmt.Printf("%s\n", string(aj))
b := []string{}
fmt.Println(b == nil)
bs := &A{
Data: b,
}
bj, _ := json.Marshal(bs)
fmt.Printf("%s\n", string(bj))
}
// true
// {"Data":null}
// false
// {"Data":[]}
This will have a big difference when this value is returned as an API response. Front end needs to treat this very differently. In general, nil slice is a preferred style.