[lug] golang gotchas

Lee Woodworth blug-mail at duboulder.com
Thu Feb 22 17:25:38 MST 2018


Speaking of golang gotchas, when I started using go, nil
handling was sometimes puzzling.

Given these declarations:

     type T struct { a, b int }

     var ptr1 *T = nil
     var ptr2 *T /* effectively (*T) (0) */
     var ifval1 interface{} = T{ a: 1, b: 2 }
     var ifval2 interface{} = &T{ a: 3, b: 4 }
     var ifval3 interface{} = nil
     var ifval4 interface{} = ptr1

The output of fmt.Printf using format specs of "%T/%p %v" for the values
and "%t" for the comparisons:

     ptr1=*main.T/0x0 <nil>
     ptr2=*main.T/0x0 <nil>
         ptr1 == ptr2 is true
         ptr1 == nil is true
     ifval1=main.T/%!p(main.T={1 2}) {1 2}
     ifval2=*main.T/0xc420018100 &{3 4}
     ifval3=<nil>/%!p(<nil>) <nil>
     ifval4=*main.T/0x0 <nil>
         ifval3 == nil is true
         ifval4 == nil is false

The quirky thing is
     ptr1 = nil; ifval4 = ptr1
but
     ptr1 == nil and ifval4 != nil

This is due to interfaces holding a type and a value and untyped nil constant conversion.
     ptr1 = nil effectively converts the untyped nil to (*T) (0)
     ifval3 = nil converts the untyped nil to a special value "nil"
         with a special system-defined type
     ifval4 = ptr1 stores the type *T and ptr1's value (0) which
         is not the same as the special "nil" value

You can see that interface{} is storing a value and not a pointer
from ifval1. Its type is T, not *T and the %p format uses an error
display instead of printing an address.

Another quirk is that if type T had the pointer-based method M1 defined on it:
     func (t *T) M1 () { fmt.Println ("ok") }
then
     ptr1.M1 ()
is a perfectly valid method call and won't panic since M1 does not dereference
its receiver argument.



More information about the LUG mailing list