Engineering Blog

                            

Being confused about nil vs. empty slice

Go developers fairly frequently mix nil and empty slices. In this blog post discusses how they are differ and when to use each one.

Slices are one of the most widely used data types in Go. It provides a way for to work with ad manage collections of data, and can be appended to or iterated over. But what’s nil slice and how does it differ from an empty slice? Let’s discuss it.

In Go, a nil slice is a slice with a nil underlying array. On the other hand, an empty slice is a slice with a non-nil underlying array, but with a length of 0. This means that an empty slice has the capacity to hold elements, while nil slice does not. To be proficient with slices , we need to make sure we don’t mix these concepts.

  • A slice is empty if its length is equal to 0.
  • A slice is nil if it equals to nil.
package main

import "fmt"

func main() {
 //case 1
 var s []string
 log(1, s)

 //case 2
 s = []string(nil)
 log(2, s)

 //case3
 s = []string{}
 log(3, s)

 //case4
 s = make([]string, 0)
 log(4, s)
}

func log(i int, s []string) {
   fmt.Println("-------------------------------------------------")
   fmt.Printf("slice case %d: empty=%t\tnil=%t\n", i, len(s) == 0, s == nil)
}

All the slices are empty, meaning the length equals 0. Therefore, a nil slice is also an empty slice. However, only the first two are nil slices. If we have multiple ways to initialize a slice, which case should we favor? There are two things to note:

  • One of the main difference between a nil and empty slice regards allocation. Initializing a nil slice doesn’t require any allocation, which isn’t the case for an empty slice.
  • Regardless of whether a slice is nil, calling the append built-in function works.

For example,

var s1 []int
s1 = append(s2, 1) //[1]

Case 2 usecase, isn’t the most widely used. But it can be helpful as syntactic sugar because we can pass a nil slice in a single line—for example, using append:
s := append([]int(nil), 23)
If we had used option 1 (var s []string), it would have required two lines of code. This is probably not the most important readability optimization of all time, but it’sstill worth knowing.

We should also mention that some libraries distinguish between nil and empty slices. This is the case, for example, with the encoding/json package. The following examples marshal two structs, one containing a nil slice and the second a non-nil, empty slice:

package main import ( “encoding/json” “fmt” ) type employee struct { Name string ContactInfo []string } func main() { //nil slice var s1 []string customer1 := employee{ Name: “employee-1”, ContactInfo: s1, } b, _ := json.Marshal(customer1) fmt.Println(string(b)) //non-nil empty slice s2 := make([]string, 0) customer2 := employee{ Name: “employee-2”, ContactInfo: s2, } b, _ = json.Marshal(customer2) fmt.Println(string(b)) }

Running this example, notice that the marshaling results for these two structs are
different:

Different response result

Here, a nil slice is marshaled as a null element , whereas a non-nil, empty slice is marshaled as an empty array. If we work in the context of strict JSON clients that differentiate between null and [], it’s essential to keep this distinction in mind. In any case, while working with the standard library or external libraries, we should ensure that when using one version or another, our code doesn’t lead to unexpected results.

Conclusion

In Go, there is a distinction between nil and empty slices. A nil slice equals nil whereas and empty slice has a length of zero. A nil slice is empty, but and empty slice isn’t necessarily nil. Meanwhile, a nil slice doesn’t require any allocation. We have seen throughout this section how to initialize a slice depending on the context by using.

  • var s1 []string if aren’t sure about the final length and the slice can be empty.
  • []string(nil) as syntactic sugar to create a nil and empty slice.
  • make([]string,length) If the future length is known.
  • []string{} if we initialized a slice with some values by default.

Github Repository Link

https://github.com/Sugaml/golang-best-practice/tree/main/slice-nil-vs-empty

References

  • 100 Go Mistakes and how to avoid them, Teiva Harsanyi, Manning Publications Co
  • https://freshman.tech/snippets/go/nil-vs-empty-slices
Previous Post
Next Post