Empty slice vs nil slice in GoLang

English 简体中文 繁体中文 ภาษาไทย Tiếng Việt
Summary

Go's slice type presents a subtle but important distinction between nil and empty slices, even though both have zero length and capacity. A nil slice, which is the zero value for a slice, lacks an underlying array and evaluates as `nil`, typically declared with `var s []T`. Conversely, an empty slice, created using `[]T{}` or `make([]T, 0)`, possesses a zero-length underlying array and does not evaluate as `nil`. This difference is particularly critical during JSON encoding, where a nil slice marshals to `null` while an empty slice marshals to `[]`, significantly impacting API responses and frontend data handling. Therefore, understanding this behavior is essential, with nil slices generally being the preferred style.

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.

JSON GOLANG EMPTY SLICE NIL SLICE

  RELATED

  COMMENTS

0

No comment for this article.