Featured image of post Nil slice versus empty slice in Go

Nil slice versus empty slice in Go

Don't fear the nil slice!

When starting to code in Go, we encountered the following situation. We needed to create an empty slice, so we did:

slice := []string{}

However, my IDE flagged it as a warning, and pointed me to this Go style guide passage, which recommended using a nil slice instead:

var slice []string

This recommendation didn’t seem right. How can a nil variable be better? Won’t we run into issues like null pointer exceptions and other annoyances? Well, as it turns out, that’s not how slices work in Go. When declaring a nil slice, it is not the dreaded null pointer. It is still a slice. This slice includes a slice header, but its value just happens to be nil.

The main difference between a nil slice and an empty slice is the following. A nil slice compared to nil will return true. That’s pretty much it.

if slice == nil {
	fmt.Println("Slice is nil.")
} else {
	fmt.Println("Slice is NOT nil.")
}

When printing a nil slice, it will print like an empty slice:

fmt.Printf("Slice is: %v\n", slice)
Slice is: []

You can append to a nil slice:

slice = append(slice, "bozo")

You can loop over a nil slice, and the code will not enter the for loop:

for range slice {
	fmt.Println("We are in a for loop.")
}

The length of a nil slice is 0:

fmt.Printf("len: %#v\n", len(slice))
len: 0

And, of course, you can pass a nil slice by pointer. That’s right – pass a nil slice by pointer.

func passByPointer(slice *[]string) {
    fmt.Printf("passByPointer len: %#v\n", len(*slice))
    *slice = append(*slice, "bozo")
}

You will get the updated slice if the underlying slice is reassigned.

passByPointer(&slice)
fmt.Printf("len after passByPointer: %#v\n", len(slice))
len after passByPointer: 1

The code above demonstrates that a nil slice is not a nil pointer. On the other hand, you cannot dereference a nil pointer like you can a nil slice. This code causes a crash:

var nullSlice *[]string
fmt.Printf("Crash: %#v\n", len(*nullSlice))

Here’s the full gist:

Further reading

Watch nil slice vs empty slice video