The zero value of a slice is not nil

35.2k views Asked by At

I was following the example https://tour.golang.org/moretypes/10 I modified the code expecting to get the same result. I did not. Is this a bug, or a documentation error? The tour states

A nil slice has a length and capacity of 0.

My y variable has a length and capacity of 0.

package main

import "fmt"

func myPrint(z []int) {
    fmt.Println(z, len(z), cap(z))
    if z == nil {
        fmt.Println("nil!")
    }
}

func main() {
    var z  []int 
    y := []int {}
    myPrint(z)
    myPrint(y)
}

Here is my output.

[] 0 0
nil!
[] 0 0

I was expecting a second "nil"~ Why didn't I get it?

5

There are 5 answers

2
tomasz On BEST ANSWER

The doc you referenced states that a nil slice has a length and capacity of 0, but not that every slice of length and capacity of zero is a nil slice. The specification only says that the value of an uninitialized slice is nil.

This is a convenience to support len and cap on slices which are uninitialised (nil). Otherwise we would need to check for non-nil first in order to avoid panic. (This also holds for other in-built types like maps or channels.)

In terms of the fmt.Print output, the difference in behaviour is similar to printing an uninitialised (nil) pointer vs pointer to an empty structure:

var s *struct{} // uninitialised pointer
fmt.Println(s)  // <nil>

s = &struct{}{} // pointer to an empty structure
fmt.Println(s)  // &{}
0
Akavall On

In this case:

var z []int 

You have declared a variable z but you did not initialize it.

In this case:

y := []int {}

You declared it and initialized it, you set it to an empty slice. Writing the second expression the long way makes the difference between the two expressions more clear:

var y []int = []int {}
1
JimB On

Your y variable isn't the zero value for a slice. It's allocated via an empty slice literal.

// both of these allocate a slice
y := []int{}
z := []int{1, 2, 3}
1
tgogos On

nil Vs empty slice

If we think of a slice like this:

[pointer] [length] [capacity]

then:

nil slice:   [nil][0][0]
empty slice: [addr][0][0] // it points to an address

From: "Go in action" book:

nil slice

They’re useful when you want to represent a slice that doesn’t exist, such as when an exception occurs in a function that returns a slice.

// Create a nil slice of integers.
var slice []int

empty slice

Empty slices are useful when you want to represent an empty collection, such as when a database query returns zero results.

// Use make to create an empty slice of integers.
slice := make([]int, 0)

// Use a slice literal to create an empty slice of integers.
slice := []int{}

Regardless of whether you’re using a nil slice or an empty slice, the built-in functions append, len, and cap work the same.


Go playground example:

package main

import (
    "fmt"
)

func main() {

    var nil_slice []int
    var empty_slice = []int{}

    fmt.Println(nil_slice == nil, len(nil_slice), cap(nil_slice))
    fmt.Println(empty_slice == nil, len(empty_slice), cap(empty_slice))

}

prints:

true 0 0
false 0 0
0
pete lin On

A nil slice has a length and capacity of 0 and has no underlying array.

var s []string => no underlying array var s = []string => create a underlying array but his length is 0.