Pro Yearly is on sale from $80 to $50! »

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.

D8e5d79ca42edc07693b9c1aacaa7e5e?s=128

Francesc Campoy Flores

July 11, 2016
Tweet

Transcript

  1. understanding nil @francesc

  2. thanks

  3. welcome every single one of you

  4. agenda what is nil? what is nil in Go? what

    does nil mean? is nil useful?
  5. nil? you misspelled null

  6. how I learn words

  7. how I learn words

  8. etymology nil Latin nihil meaning nothing null Latin ne +

    ullus meaning not any none Old English ne + ān meaning not one
  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
  10. nil is (a) zero

  11. interlude a bit of history

  12. None
  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
  14. panic: runtime error: invalid memory address or nil pointer dereference

  15. Uncaught TypeError: undefined is not a function

  16. None
  17. nil leads to panic

  18. panic leads to fear by danpawley on Flickr

  19. fear leads to by korymatthew on Flickr

  20. λ functional programming

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

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

  23. </interlude>

  24. zero values what are they?

  25. bool → false numbers→ 0 string → "" zero values

    pointers → nil slices → nil maps → nil channels → nil functions → nil interfaces → nil
  26. zero values for struct types type Person struct { AgeYears

    int Name string Friend []Person } var p Person // Person{0, "", nil}
  27. the type of nil

  28. “... unless the value is the predeclared identifier nil, which

    has no type” - Go language specification
  29. untyped zero a := false a := "" a :=

    0 // a := int(0) a := 0.0 // a := float64(0) a := nil // use of untyped nil
  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
  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
  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
  33. // YOLO var nil = errors.New(“¯\_(ツ)_/¯”) * For extra evilness:

    place at the end of doc.go predefined vs keyword
  34. zero values what do they mean?

  35. pointers slices maps channels functions interfaces kinds of nil

  36. pointers slices maps channels functions interfaces kinds of nil

  37. - they point to a position in memory - similar

    to C or C++, but - no pointer arithmetic → memory safety - garbage collection pointers in Go
  38. - points to nil a.k.a. nothing - zero value of

    pointers nil pointer
  39. pointers slices maps channels functions interfaces kinds of nil

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

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

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

    len cap
  43. pointers slices maps channels functions interfaces kinds of nil

  44. channels, maps, and functions ptr *something

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

    be cloud shaped
  46. channels, maps, and functions ptr nil

  47. pointers slices maps channels functions interfaces kinds of nil

  48. (type, value)

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

    // true
  50. (nil, nil) equals nil

  51. var p *Person // nil of type *Person var s

    fmt.Stringer = p // Stringer (*Person, nil) fmt.Println(s == nil) // false
  52. doesn’t equal nil (*Person, nil)

  53. when is nil not nil?

  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
  55. do not declare concrete error vars

  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
  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
  58. do not return concrete error types

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

  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
  61. “Make the zero value useful” - Rob Pike in his

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

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

  64. var p *int p == nil // true *p //

    panic: invalid memory address or nil pointer dereference pointers
  65. coding time

  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
  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
  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
  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
  70. nil receivers are useful 零

  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
  72. func (t *tree) String() string { if t == nil

    { return "" } return fmt.Sprint(t.l, t.v, t.r) } nil receivers are useful: String
  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
  74. keep nil useful if possible, if not NewX()

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

  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
  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
  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
  79. use nil slices they’re often fast enough

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

  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
  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 }
  83. NewGet( "http://google.com", map[string]string{ "USER_AGENT": "golang/gopher", }, ) using maps

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

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

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

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

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

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

  90. use nil maps as read-only empty maps

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

  92. var c chan t <- c // blocks forever c

    <- x // blocks forever close(c) // panic: close of nil channel nil channels
  93. coding time

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

    out
  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
  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
  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
  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
  99. checking for closed chans case v, ok := <-a: if

    !ok { aClosed = true continue } out <- v // analogous code for case b
  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 } } }
  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
  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) }
  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) }
  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
  105. ship it! Fancy Gopher by Renee French

  106. by hzeller on Flickr

  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
  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
  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
  110. can we switch off a chan?

  111. var c chan t <- c // blocks forever c

    <- x // blocks forever close(c) // panic: close of nil channel nil channels
  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
  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
  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
  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
  116. fancy party time! Fancy Gopher by Renee French

  117. use nil chans to disable a select case

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

  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 }
  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
  121. pointers slices maps channels functions interfaces how are these useful?

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

    != nil { … } interfaces
  123. why does nil *Person not equal nil interface? 零

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

  125. summer var t *tree var s Summer = t fmt.Println(t

    == nil, s.Sum()) // true 0
  126. type ints []int func (i ints) Sum() int { s

    := 0 for _, v := range i { s += v } return s } summer
  127. summer var i ints var s Summer = i fmt.Println(i

    == nil, s.Sum()) // true 0
  128. nil values can satisfy interfaces 零

  129. func doSum(s Summer) int { if s == nil {

    return 0 } return s.Sum() } nil values and default values
  130. nil values and default values var t *tree doSum(t) //

    (*tree, nil) var i ints doSum(i) // (ints, nil) doSum(nil) // (nil, nil)
  131. http.ListenAndServe("localhost:8080", nil) nil values and default values

  132. use nil interfaces to signal default 零

  133. None
  134. nil is useful

  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
  136. nil is an important part Go 零

  137. let’s not avoid nil

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