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)
Nil
s differ if their types differ
As with any other values of different types, typed nil
s 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 nil
s 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
- Russ Cox on Interfaces
- Go Gotchas
- Understanding Nil by Francesc Campoy Flores
- Go source
Thanks!