Technical Article => Programming => Go

nil check is frequently seen in GoLang code especially for error check since GoLang's special error handling convention. In most cases, nil check is straight forward, but in interface case, it's a bit different and special care needs to be taken.

Take a look at below code snippet and guess what the output will be.

package main import ( "bytes" "fmt" "io" ) func check(w io.Writer) { if w != nil { fmt.Println("w is not nil") } fmt.Printf("w is %+v

", w) } func main() { var b *bytes.Buffer check(b) fmt.Printf("b is %+v", b) }

The output will be:

w is not nil w is b is

In the check() method, you would expect that w would be nil in the case. But actually it's not. And when printing the object, it becomes nil. How can this happen?

The reason is that interface has a special implementation which it contains two components: a type and a value. Under the cover, interfaces are implemented as a type T and a value V. V is a concrete value such as an int, struct or pointer, never an interface itself, and has type T. For instance, if we store the int value 3 in an interface, the resulting interface value has, schematically, (T=int, V=3). The value V is also known as the interface's dynamic value, since a given interface variable might hold different values V (and corresponding types T) during the execution of the program.

An interface value is nil only if the V and T are both unset, (T=nil, V is not set), In particular, a nil interface will always hold a nil type. If we store a nil pointer of type *int inside an interface value, the inner type will be *int regardless of the value of the pointer: (T=*int, V=nil). Such an interface value will therefore be non-nil even when the pointer value V inside is nil.

So in the above case, when creating the variable b, it has a type of *bytes.Buffer but its value is nil. Hence you would see above output. Let's see a more specific example on when an interface would be nil.

package main import ( "fmt" ) type SomeError struct{} func (se *SomeError) Error() string { return "error" } func check(e error) { if e == nil { fmt.Println("e is nil") } fmt.Printf("e is %+v

", e) } func main() { var e error = nil check(e) var se *SomeError = nil check(se) }

When creating variable e, it's an error but with no specific error type. And it has a value of nil. Hence the comparison of it to nil will return true.

e is nil e is <nil> e is error

Be very careful when using interface as function parameter and doing nil check, it may not return what you expect.