Understanding Go's `nil` value

This post raises more questions than answers (for me). While I’m learning more about what Go’s nil is I’ll be making assumptions based on how the languages behaves. I’ll try to collect the answers from the Go’s source code, but in the mean time: just plain assumptions, feel free correct.

Nil is a typed value

Type information is inherent part of a nil value in Go. Lack of type information is a compilation error:

package main
func main() {
 println(nil)
}
// Errors with: use of untyped nil

Adding a type information satisfies compiler:

package main
func main() {
  println(interface{}(nil))
}

or this

package main
import "fmt"
func main() {
  fmt.Println(nil)
}

(type information, in this case, is inferred from the fmt.Println arguments’ type I assume)

Nils differ if their types differ

As with any other values of different types, typed nils cannot be compared:

package main
func main() {
  println(interface{}(nil) == (*int)(nil))
}
// Compilation error

Similarly it’s possible to convert nil to a “common” empty interface type, but because nils in below examples are of different types - they’re not equal: nil value of func() type gets converted to non-nil value of interface{} type - they’re not equal.

package main
func main() {
  println(nil == interface{}((func())(nil)))
}
// Outputs: false

This is quite confusing

package main
import "os"
func main() {
  println(nil == error((*os.PathError)(nil)))
}
// Outputs: false

Nil is a Zero Value for channels, slices, maps, interfaces, function, and pointer types

Zero Value is a default value which is obtained by declaring a variable without initializing it.

Declaring a boolean value initializes it to false, declaring one of “nil-able types” initializes it to Zero Value which is corresponding (typed) nil value.

package main
import "fmt"
func main() {
  fmt.Printf("Func type nil:%#v\n",(func())(nil))
  fmt.Printf("Map type nil:%#v\n", map[string]string(nil))
  fmt.Printf("Slice type nil:%#v\n", []string(nil))
  fmt.Printf("Interface{} type nil:%#v\n", nil)
  fmt.Printf("Channel type nil:%#v\n", (chan struct{})(nil))
  fmt.Printf("Pointer type nil:%#v\n", (*struct{})(nil))
  fmt.Printf("Pointer type nil:%#v\n", (*int)(nil))
}
// Outputs:
// Func type Nil:(func())(nil)
// Map type Nil:map[string]string(nil)
// Slice type Nil:[]string(nil)
// Interface{} type nil:<nil>
// Channel type nil:(chan struct {})(nil)
// Pointer type nil:(*struct {})(nil)
// Pointer type nil:(*int)(nil)

%#v is a handy verb for printing out nil value with its type, useful for debugging nil related confusions.

Reading

Thanks!

Comments