[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