nil
s in Gonil
is a frequently used and important predeclared identifier in Go. It is the literal representation of zero values of many kinds of types. Many new Go programmers with experiences of some other popular languages may view nil
as the counterpart of null
(or NULL
) in other languages. This is partly right, but there are many differences between nil
in Go and null
(or NULL
) in other languages.
nil
.
nil
Is a Predeclared Identifier in Gonil
without declaring it.
nil
Can Represent Zero Values of Many Typesnil
can represent zero values of the following kinds of types:
nil
Has Not a Default Typetrue
and false
are both bool
type.
iota
is int
.
nil
has not a default type, though it has many possible types. In fact, the predeclared nil
is the only untyped value who has not a default type in Go. There must be sufficient information for compiler to deduce the type of a nil
from context.
package main
func main() {
// There must be sufficient information for
// compiler to deduce the type of a nil value.
_ = (*struct{})(nil)
_ = []int(nil)
_ = map[int]bool(nil)
_ = chan string(nil)
_ = (func())(nil)
_ = interface{}(nil)
// These lines are equivalent to the above lines.
var _ *struct{} = nil
var _ []int = nil
var _ map[int]bool = nil
var _ chan string = nil
var _ func() = nil
var _ interface{} = nil
// This following line doesn't compile.
var _ = nil
}
nil
Is Not a Keyword in Gonil
can be shadowed.
package main
import "fmt"
func main() {
nil := 123
fmt.Println(nil) // 123
// The following line fails to compile,
// for nil represents an int value now
// in this scope.
var _ map[string]int = nil
}
null
and NULL
in many other languages are also not keywords.)
nil
). The size of a nil value is always the same as the sizes of non-nil values whose types are the same as the nil value. But nil values of different kinds of types may have different sizes.
package main
import (
"fmt"
"unsafe"
)
func main() {
var p *struct{} = nil
fmt.Println( unsafe.Sizeof( p ) ) // 8
var s []int = nil
fmt.Println( unsafe.Sizeof( s ) ) // 24
var m map[int]bool = nil
fmt.Println( unsafe.Sizeof( m ) ) // 8
var c chan string = nil
fmt.Println( unsafe.Sizeof( c ) ) // 8
var f func() = nil
fmt.Println( unsafe.Sizeof( f ) ) // 8
var i interface{} = nil
fmt.Println( unsafe.Sizeof( i ) ) // 16
}
nil
are always the same. For example, the sizes of all values of all different slice types are the same.
// Compilation failure reason: mismatched types.
var _ = (*int)(nil) == (*bool)(nil) // error
var _ = (chan int)(nil) == (chan bool)(nil) // error
nil
values are not exceptions of the comparison rules.
type IntPtr *int
// The underlying of type IntPtr is *int.
var _ = IntPtr(nil) == (*int)(nil)
// Every type in Go implements interface{} type.
var _ = (interface{})(nil) == (*int)(nil)
// Values of a directional channel type can be
// converted to the bidirectional channel type
// which has the same element type.
var _ = (chan int)(nil) == (chan<- int)(nil)
var _ = (chan int)(nil) == (<-chan int)(nil)
var _ = ([]int)(nil) == ([]int)(nil)
var _ = (map[string]int)(nil) == (map[string]int)(nil)
var _ = (func())(nil) == (func())(nil)
nil
identifier.
// The following lines compile okay.
var _ = ([]int)(nil) == nil
var _ = (map[string]int)(nil) == nil
var _ = (func())(nil) == nil
false
. The reason is the non-interface value will be converted to the type of the interface value before making the comparison. The converted interface value has a dynamic type but the other interface value has not. That is why the comparison result is always false
.
fmt.Println( (interface{})(nil) == (*int)(nil) ) // false
fmt.Println( (map[string]int)(nil)["key"] ) // 0
fmt.Println( (map[int]bool)(nil)[123] ) // false
fmt.Println( (map[int]*int64)(nil)[123] ) // <nil>
0
, 1
, 2
, 3
and 4
, then block for ever. Hello
, world
and Bye
will never be printed.
for range []int(nil) {
fmt.Println("Hello")
}
for range map[string]string(nil) {
fmt.Println("world")
}
for i := range (*[5]int)(nil) {
fmt.Println(i)
}
for range chan bool(nil) { // block here
fmt.Println("Bye")
}
package main
type Slice []bool
func (s Slice) Length() int {
return len(s)
}
func (s Slice) Modify(i int, x bool) {
s[i] = x // panic if s is nil
}
func (p *Slice) DoNothing() {
}
func (p *Slice) Append(x bool) {
*p = append(*p, x) // panic if p is nil
}
func main() {
// The following selectors will not cause panics.
_ = ((Slice)(nil)).Length
_ = ((Slice)(nil)).Modify
_ = ((*Slice)(nil)).DoNothing
_ = ((*Slice)(nil)).Append
// The following two lines will also not panic.
_ = ((Slice)(nil)).Length()
((*Slice)(nil)).DoNothing()
// The following two lines will panic. But panics
// will not be triggered at the time of invoking
// the methods. They will be triggered on
// dereferencing nil pointers in the method bodies.
/*
((Slice)(nil)).Modify(0, true)
((*Slice)(nil)).Append(true)
*/
}
Append
method is not perfect, it should be modified as the following one.
func (p *Slice) Append(x bool) {
if p == nil {
*p = []bool{x}
return
}
*p = append(*p, x)
}
*new(T)
Results a Nil T
Value if the Zero Value of Type T
Is Represented With the Predeclared nil
Identifierpackage main
import "fmt"
func main() {
fmt.Println(*new(*int) == nil) // true
fmt.Println(*new([]int) == nil) // true
fmt.Println(*new(map[int]bool) == nil) // true
fmt.Println(*new(chan string) == nil) // true
fmt.Println(*new(func()) == nil) // true
fmt.Println(*new(interface{}) == nil) // true
}
nil
is designed as an identifier which can be used to represent the zero values of some kinds of types. It is not a single value. It can represent many values with different memory layouts.
The Go 101 project is hosted on Github. Welcome to improve Go 101 articles by submitting corrections for all kinds of mistakes, such as typos, grammar errors, wording inaccuracies, description flaws, code bugs and broken links.
If you would like to learn some Go details and facts every serveral days, please follow Go 101's official Twitter account @zigo_101.
reflect
standard package.sync
standard package.sync/atomic
standard package.