The difference between nil and empty slices in Go
The difference between nil and empty slices are a common point of confusion for newbies to Go. This post discusses how they differ and when to use each one.
Slices are one of the most common data types used in Go. They provide a way for to work with and manage collections of data, and can be appended to or iterated over. But what’s a nil slice and how does it differ from an empty slice? Let’s find out.
Nil slices
A nil slice is one that is declared without being initialised. It has a length and capacity of 0
with no underlying array, and it’s zero value is nil
. Nil slices are useful to represent a collection that does not exist yet.
// nil slice
var slice []string
slice == nil // true
Empty slice
An empty slice is one that contains zero elements. It has an underlying array, but with zero elements. An empty slice is handy for representing an empty collection, such as when a query yields no results.
// Empty slice
var slice = []string{}
// or
var slice = make([]string, 0)
slice == nil // false
What’s the difference?
For the most part, there’s actually no observable difference between nil slices and empty slices. The built-in functions append
, len
, and cap
all work in the same way for both, and you can for...range
over either with the same result (0 iterations).
var s1 []int
s2 := []int{}
fmt.Println(len(s1)) // 0
fmt.Println(len(s2)) // 0
s1 = append(s1, 1)
s2 = append(s2, 2)
fmt.Println(len(s1)) // 1
fmt.Println(len(s2)) // 1
There’s a subtle difference though. As mentioned earlier, nil
slices are great for collections that do not exist yet, while empty slices are for empty collections. This is exemplified when encoding to JSON:
// https://play.golang.org/p/PFUHW5d-26u
package main
import (
"encoding/json"
"fmt"
)
type Response struct {
Result []int `json:"result"`
}
func main() {
// nil slice
var s1 []int
r1 := &Response{
Result: s1,
}
b, _ := json.Marshal(r1)
fmt.Printf("%+v\n", string(b)) // {"result":null}
s2 := []int{}
r2 := &Response{
Result: s2,
}
b, _ = json.Marshal(r2)
fmt.Printf("%+v\n", string(b)) // {"result":[]}
}
As you can see, the nil
slice gets encoded as null
while the empty slice becomes an empty JSON array. You need to guard against returning the wrong value from your API, as it could easily violate your API spec leading to bugs and unhappy customers.
Thanks for reading, and happy coding!