$30 off During Our Annual Pro Sale. View Details »

Understanding Nil

Understanding Nil

Opening Keynote at GopherCon 2016

Is it a constant? A variable? Where is it defined? What is its type? It has no type? It has all the types? Those are usual questions that people learning Go ask and this talk answers all of them and more.

The talk covers the different contexts where nil is used, what it means, the gotchas to be aware of, and some patterns where nil becomes essential.

In parallel to the exploration of how nil can be used I will also discuss the origin and history of nil in those contexts.

Francesc Campoy Flores

July 11, 2016
Tweet

More Decks by Francesc Campoy Flores

Other Decks in Programming

Transcript

  1. understanding nil
    @francesc

    View Slide

  2. thanks

    View Slide

  3. welcome
    every single one of you

    View Slide

  4. agenda
    what is nil?
    what is nil in Go?
    what does nil mean?
    is nil useful?

    View Slide

  5. nil?
    you misspelled null

    View Slide

  6. how I learn words

    View Slide

  7. how I learn words

    View Slide

  8. etymology
    nil
    Latin nihil meaning nothing
    null
    Latin ne + ullus meaning not any
    none
    Old English ne + ān meaning not one

    View Slide

  9. names for the number zero in English
    - zero - cipher
    - null - love
    - duck - nil
    - nada - zilch
    - zip - the letter ‘o’
    - naught - nought
    - aught - ought
    source: wikipedia

    View Slide

  10. nil is (a) zero

    View Slide

  11. interlude
    a bit of history

    View Slide

  12. View Slide

  13. “I call it my billion-dollar mistake. It was
    the invention of the null reference in
    1965.
    At that time, I was designing the first
    comprehensive type system for
    references in an object oriented
    language.”
    - Sir C.A.R. Hoare

    View Slide

  14. panic: runtime error:
    invalid memory address or nil pointer
    dereference

    View Slide

  15. Uncaught TypeError:
    undefined is not a function

    View Slide

  16. View Slide

  17. nil leads to panic

    View Slide

  18. panic leads to fear
    by danpawley on Flickr

    View Slide

  19. fear leads to
    by korymatthew on Flickr

    View Slide

  20. λ
    functional programming

    View Slide

  21. “Functional Go?
    Thanks, but no”
    - francesc at dotGo.eu 2015

    View Slide

  22. there are many ways,
    nil is the Go way

    View Slide


  23. View Slide

  24. zero values
    what are they?

    View Slide

  25. bool → false
    numbers→ 0
    string → ""
    zero values
    pointers → nil
    slices → nil
    maps → nil
    channels → nil
    functions → nil
    interfaces → nil

    View Slide

  26. zero values for struct types
    type Person struct {
    AgeYears int
    Name string
    Friend []Person
    }
    var p Person // Person{0, "", nil}

    View Slide

  27. the type of nil

    View Slide

  28. “... unless the value is the
    predeclared identifier nil,
    which has no type”
    - Go language specification

    View Slide

  29. untyped zero
    a := false
    a := ""
    a := 0 // a := int(0)
    a := 0.0 // a := float64(0)
    a := nil // use of untyped nil

    View Slide

  30. “nil is a predeclared
    identifier representing the
    zero value for a pointer,
    channel, func, interface,
    map, or slice type.”
    - Go documentation for “builtin” package

    View Slide

  31. “nil is a predeclared
    identifier representing the
    zero value for a pointer,
    channel, func, interface,
    map, or slice type.”
    - Go documentation for “builtin” package

    View Slide

  32. twenty-five* keywords
    break default func interface select
    case defer go map struct
    chan else goto package switch
    const fallthrough if range type
    continue for import return var
    * plus the five secret ones, obviously

    View Slide

  33. // YOLO
    var nil = errors.New(“¯\_(ツ)_/¯”)
    * For extra evilness: place at the end of doc.go
    predefined vs keyword

    View Slide

  34. zero values
    what do they mean?

    View Slide

  35. pointers
    slices
    maps
    channels
    functions
    interfaces
    kinds of nil

    View Slide

  36. pointers
    slices
    maps
    channels
    functions
    interfaces
    kinds of nil

    View Slide

  37. - they point to a position in memory
    - similar to C or C++, but
    - no pointer arithmetic → memory safety
    - garbage collection
    pointers in Go

    View Slide

  38. - points to nil a.k.a. nothing
    - zero value of pointers
    nil pointer

    View Slide

  39. pointers
    slices
    maps
    channels
    functions
    interfaces
    kinds of nil

    View Slide

  40. slice internals
    ptr
    len
    cap
    *elem
    int
    int
    []byte

    View Slide

  41. a slice with five elements
    s := make([]byte, 5)
    5
    5 0 0 0 0 0
    []byte
    [5]byte
    ptr
    len
    cap

    View Slide

  42. nil slice
    var s []byte
    nil
    0
    0
    []byte
    ptr
    len
    cap

    View Slide

  43. pointers
    slices
    maps
    channels
    functions
    interfaces
    kinds of nil

    View Slide

  44. channels, maps, and functions
    ptr *something

    View Slide

  45. channels, maps, and functions
    ptr implementation
    * implementations might not be cloud shaped

    View Slide

  46. channels, maps, and functions
    ptr nil

    View Slide

  47. pointers
    slices
    maps
    channels
    functions
    interfaces
    kinds of nil

    View Slide

  48. (type, value)

    View Slide

  49. var s fmt.Stringer // Stringer (nil, nil)
    fmt.Println(s == nil) // true

    View Slide

  50. (nil, nil)
    equals nil

    View Slide

  51. var p *Person // nil of type *Person
    var s fmt.Stringer = p // Stringer (*Person, nil)
    fmt.Println(s == nil) // false

    View Slide

  52. doesn’t equal nil
    (*Person, nil)

    View Slide

  53. when is nil not nil?

    View Slide

  54. func do() error {
    var err *doError
    return err
    }
    func main() {
    err := do()
    fmt.Println(err == nil)
    }
    when is nil not nil?
    // nil of type *doError
    // error (*doError, nil)
    // error (*doError, nil)
    // false

    View Slide

  55. do not declare
    concrete error vars

    View Slide

  56. func do() *doError {
    return nil
    }
    func main() {
    err := do()
    fmt.Println(err == nil)
    }
    // nil of type *doError
    // nil of type *doError
    // true
    nil is not nil

    View Slide

  57. func do() *doError {
    return nil
    }
    func wrapDo() error {
    return do()
    }
    func main() {
    err := wrapDo()
    fmt.Println(err == nil)
    }
    nil is not nil
    // nil of type *doError
    // error (*doError, nil)
    // nil of type *doError
    // error (*doError, nil)
    // false

    View Slide

  58. do not return
    concrete error types

    View Slide

  59. * for some kinds of nil
    nil is not nil

    View Slide

  60. pointers point to nothing
    slices have no backing array
    maps are not initialized
    channels are not initialized
    functions are not initialized
    interfaces have no value assigned, not even a nil pointer
    kinds of nil

    View Slide

  61. “Make the zero value
    useful”
    - Rob Pike in his Go Proverbs

    View Slide

  62. pointers
    slices
    maps
    channels
    functions
    interfaces
    how are these useful?

    View Slide

  63. pointers
    slices
    maps
    channels
    functions
    interfaces
    how are these useful?

    View Slide

  64. var p *int
    p == nil // true
    *p // panic: invalid memory address
    or nil pointer dereference
    pointers

    View Slide

  65. coding time

    View Slide

  66. type tree struct {
    v int
    l *tree
    r *tree
    }
    func (t *tree) Sum() int
    implement Sum
    6
    2
    1
    7
    3
    4
    5 8
    9

    View Slide

  67. a first solution
    func (t *tree) Sum() int {
    sum := t.v
    if t.l != nil {
    sum += t.l.Sum()
    }
    if t.r != nil {
    sum += t.r.Sum()
    }
    return sum
    }
    6
    2
    1
    7
    3
    4
    5 8
    9

    View Slide

  68. Code repetition:
    if v != nil {
    v.m()
    }
    Panic when t is nil
    var t *tree
    sum := t.Sum() // panic: invalid memory address
    or nil pointer dereference
    issues

    View Slide

  69. type person struct {}
    func sayHi(p *person) { fmt.Println(“hi”) }
    func (p *person) sayHi() { fmt.Println(“hi”) }
    var p *person
    p.sayHi() // hi
    pointer receivers

    View Slide

  70. nil receivers are useful

    View Slide

  71. func (t *tree) Sum() int {
    if t == nil {
    return 0
    }
    return t.v + t.l.Sum() + t.r.Sum()
    }
    nil receivers are useful: Sum

    View Slide

  72. func (t *tree) String() string {
    if t == nil {
    return ""
    }
    return fmt.Sprint(t.l, t.v, t.r)
    }
    nil receivers are useful: String

    View Slide

  73. func (t *tree) Find(v int) bool {
    if t == nil {
    return false
    }
    return t.v == v || t.l.Find(v) || t.r.Find(v)
    }
    nil receivers are useful: Find

    View Slide

  74. keep nil useful
    if possible, if not NewX()

    View Slide

  75. pointers
    slices
    maps
    channels
    functions
    interfaces
    how are these useful?

    View Slide

  76. var s []slice
    len(s) // 0
    cap(s) // 0
    for range s // iterates zero times
    s[i] // panic: index out of range
    nil slices

    View Slide

  77. var s []int
    for i := 0; i < 10; i++ {
    fmt.Printf("len: %2d cap: %2d\n", len(s), cap(s))
    s = append(s, i)
    }
    append on nil slices

    View Slide

  78. len: 0 cap: 0 []
    len: 1 cap: 1 [0]
    len: 2 cap: 2 [0 1]
    len: 3 cap: 4 [0 1 2]
    len: 4 cap: 4 [0 1 2 3]
    len: 5 cap: 8 [0 1 2 3 4]
    len: 6 cap: 8 [0 1 2 3 4 5]
    len: 7 cap: 8 [0 1 2 3 4 5 6]
    len: 8 cap: 8 [0 1 2 3 4 5 6 7]
    len: 9 cap: 16 [0 1 2 3 4 5 6 7 8]
    → s is nil!
    → allocation
    → reallocation
    → reallocation
    → reallocation
    → reallocation
    $ go run slices.go

    View Slide

  79. use nil slices
    they’re often fast enough

    View Slide

  80. pointers
    slices
    maps
    channels
    functions
    interfaces
    how are these useful?

    View Slide

  81. nil maps
    var m map[t]u
    len(m) // 0
    for range m // iterates zero times
    v, ok := m[i] // zero(u), false
    m[i] = x // panic: assignment to entry
    in nil map

    View Slide

  82. using maps
    func NewGet(url string, headers map[string]string) (*http.Request, error) {
    req, err := http.NewRequest(http.MethodGet, url, nil)
    if err != nil {
    return nil, err
    }
    for k, v := range headers {
    req.Header.Set(k, v)
    }
    return req, nil
    }

    View Slide

  83. NewGet(
    "http://google.com",
    map[string]string{
    "USER_AGENT": "golang/gopher",
    },
    )
    using maps

    View Slide

  84. GET / HTTP/1.1
    Host: google.com
    User_agent: golang/gopher
    $ go run request.go

    View Slide

  85. NewGet("http://google.com", map[string]string{})
    using empty maps

    View Slide

  86. GET / HTTP/1.1
    Host: google.com
    $ go run request.go

    View Slide

  87. NewGet("http://google.com", map[string]string{})
    using empty maps

    View Slide

  88. NewGet("http://google.com", nil)
    nil maps are valid empty maps

    View Slide

  89. GET / HTTP/1.1
    Host: google.com
    $ go run request.go

    View Slide

  90. use nil maps
    as read-only empty maps

    View Slide

  91. pointers
    slices
    maps
    channels
    functions
    interfaces
    how are these useful?

    View Slide

  92. var c chan t
    <- c // blocks forever
    c <- x // blocks forever
    close(c) // panic: close of nil channel
    nil channels

    View Slide

  93. coding time

    View Slide

  94. func merge(out chan<- int, a, b <-chan int)
    a b
    out

    View Slide

  95. func merge(out chan<- int, a, b <-chan int) {
    for {
    select {
    case v := <-a:
    out <- v
    case v := <-b:
    out <- v
    }
    }
    }
    a naive solution

    View Slide

  96. 2 2 1 2 2 1 2 1 2 1 2 1 2 1 1 2 2 1 1 0 0 1 0 0 0 0 0 0 0 0
    0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
    0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
    0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
    0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
    0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 …
    $ go run naive.go

    View Slide

  97. var c chan t
    v, ok <- c // zero(t), false
    c <- x // panic: send on closed channel
    close(c) // panic: close of nil channel
    closed channels

    View Slide

  98. func merge(out chan<- int, a, b <-chan int) {
    for {
    select {
    case v := <-a:
    out <- v
    case v := <-b:
    out <- v
    }
    }
    }
    a naive solution

    View Slide

  99. checking for closed chans
    case v, ok := <-a:
    if !ok {
    aClosed = true
    continue
    }
    out <- v
    // analogous code for case b

    View Slide

  100. checking for closed chans
    func merge(out chan<- int, a, b <-chan int) {
    var aClosed, bClosed bool
    for !aClosed || !bClosed {
    select {
    case v, ok := <-a:
    if !ok { aClosed = true; continue }
    out <- v
    case v, ok := <-b:
    if !ok { bClosed = true; continue }
    out <- v
    }
    }
    }

    View Slide

  101. $ go run checkForClosed.go
    1 1 2 1 1 2 2 1 1 2 1 2 2 1 2 1 2 1 2 2
    fatal error: all goroutines are asleep - deadlock!
    goroutine 1 [chan receive]:
    main.main()
    /Users/campoy/src/github.com/campoy/talks/nil/talk/code/chans.
    go:97 +0x15a
    exit status 2

    View Slide

  102. and closing channel out
    func merge(out chan<- int, a, b <-chan int) {
    var aClosed, bClosed bool
    for !aClosed || !bClosed {
    select {
    case v, ok := <-a:
    if !ok { aClosed = true; continue }
    out <- v
    case v, ok := <-b:
    if !ok { bClosed = true; continue }
    out <- v
    }
    }
    close(out)
    }

    View Slide

  103. and closing channel out
    func merge(out chan<- int, a, b <-chan int) {
    var aClosed, bClosed bool
    for !aClosed || !bClosed {
    select {
    case v, ok := <-a:
    if !ok { aClosed = true; continue }
    out <- v
    case v, ok := <-b:
    if !ok { bClosed = true; continue }
    out <- v
    }
    }
    close(out)
    }

    View Slide

  104. 1 1 2 1 1 2 2 1 1 2 1 2 2 1 2 1 2 1 2 2
    $ go run closingOut.go

    View Slide

  105. ship it!
    Fancy Gopher by Renee French

    View Slide

  106. by hzeller on Flickr

    View Slide

  107. let’s log
    case v, ok := <-a:
    if !ok {
    aClosed = true
    fmt.Println("a is now closed")
    continue
    }
    out <- v
    // analogous code for case b

    View Slide

  108. $ go run withLogs.go
    1 1 2 1 1 2 2 1 1 2 1 2 2 1 2 1 2 1 2 2
    b is now closed

    b is now closed
    1
    b is now closed

    b is now closed
    1
    a is now closed

    View Slide

  109. var aClosed, bClosed bool
    for !aClosed || !bClosed {
    select {
    case v, ok := <-a:
    if !ok { aClosed = true; continue }
    out <- v
    case v, ok := <-b:
    if !ok { bClosed = true; continue }
    out <- v
    }
    }
    close(out)
    let’s log

    View Slide

  110. can we switch off a chan?

    View Slide

  111. var c chan t
    <- c // blocks forever
    c <- x // blocks forever
    close(c) // panic: close of nil channel
    nil channels

    View Slide

  112. switching off a channel
    case v, ok := <-a:
    if !ok {
    aClosed = true
    fmt.Println("a is now closed")
    continue
    }
    out <- v
    // analogous code for case b

    View Slide

  113. case v, ok := <-a:
    if !ok {
    a = nil
    fmt.Println("a is now closed")
    continue
    }
    out <- v
    // analogous code for case b
    switching off a channel

    View Slide

  114. func merge(out chan<- int, a, b <-chan int) {
    for a != nil || b != nil {
    select {
    case v, ok := <-a:
    if !ok { a = nil; continue }
    out <- v
    case v, ok := <-b:
    if !ok { b = nil; continue }
    out <- v
    }
    }
    close(out)
    }
    switching off a channel; no logs

    View Slide

  115. 1 1 2 1 1 2 2 1 1 2 1 2 2 1 2 1 2 1 2 2
    $ go run closingOut.go

    View Slide

  116. fancy party
    time!
    Fancy Gopher by Renee French

    View Slide

  117. use nil chans to disable a
    select case

    View Slide

  118. pointers
    slices
    maps
    channels
    functions
    interfaces
    how are these useful?

    View Slide

  119. Go has first-class functions
    functions can be used as struct fields
    they need a zero value; logically it is nil
    nil funcs
    type Foo struct {
    f func() error
    }

    View Slide

  120. lazy initialization of variables
    nil can also imply default behavior
    func NewServer(logger func(string, …interface{})) {
    if logger == nil {
    logger = log.Printf
    }
    logger("initializing %s", os.Getenv("hostname"))

    }
    nil funcs for default values

    View Slide

  121. pointers
    slices
    maps
    channels
    functions
    interfaces
    how are these useful?

    View Slide

  122. The nil interface is used as a signal
    if err != nil {

    }
    interfaces

    View Slide

  123. why does nil *Person not equal nil interface?

    View Slide

  124. summer
    type Summer interface {
    func Sum() int
    }

    View Slide

  125. summer
    var t *tree
    var s Summer = t
    fmt.Println(t == nil, s.Sum()) // true 0

    View Slide

  126. type ints []int
    func (i ints) Sum() int {
    s := 0
    for _, v := range i {
    s += v
    }
    return s
    }
    summer

    View Slide

  127. summer
    var i ints
    var s Summer = i
    fmt.Println(i == nil, s.Sum()) // true 0

    View Slide

  128. nil values can satisfy
    interfaces

    View Slide

  129. func doSum(s Summer) int {
    if s == nil {
    return 0
    }
    return s.Sum()
    }
    nil values and default values

    View Slide

  130. nil values and default values
    var t *tree
    doSum(t) // (*tree, nil)
    var i ints
    doSum(i) // (ints, nil)
    doSum(nil) // (nil, nil)

    View Slide

  131. http.ListenAndServe("localhost:8080", nil)
    nil values and default values

    View Slide

  132. use nil interfaces
    to signal default

    View Slide

  133. View Slide

  134. nil is useful

    View Slide

  135. pointers methods can be called on nil receivers
    slices perfectly valid zero values
    maps perfect as read-only values
    channels essential for some concurrency patterns
    functions needed for completeness
    interfaces the most used signal in Go (err != nil)
    nil is useful

    View Slide

  136. nil is an important part Go

    View Slide

  137. let’s not avoid nil

    View Slide

  138. let’s embrace nil
    Thanks, @francesc
    golang.org/s/nil

    View Slide