Go tip of the week: How to trivially test interface values for nil

A common foot sling for Go newcomers are interface variables that get a nil pointer value assigned. While that interface now contains a pointer value that is nil, the interface variable itself is not nil.

Black Friday Special:
My course Master Go is $60 off until Dec 2nd.
Hop over to AppliedGo.com for more info.

Here is what I mean: The following code snippet

  • creates x as a pointer to an integer that is nil by default,
  • creates y as an empty interface that is also nil by default,
  • and assigns x to y.
var x *int
var y any
y = x

Now the interface variable y is not nil anymore, because we assigned a variable to it. However, the variable, x, is still nil.

Pop quiz: what does y == nil yield? true or false?

An interface is like a struct with a nillable field

A common misconception is that an interface transparently represents the value that got assigned to it, hence y == nil would yield true. That's not the case.

Think of the interface as a container for the value, similar to a struct. If you create a struct with a pointer field, it seems quite natural that the struct is not nil, regardless whether the contained field is nil or not.

type Z struct {
	x *int
}
var x *int
var z Z
z.x = x

The field selector z.x makes it pretty obvious that we don't assign nil to the whole struct. With an interface, this circumstance is not that obvious. But technically, both scenarios are quite close together.

How to test the value behind an interface for nil

So our interface y is not nil, but the value it holds is nil. How can we test that value for nilness?

Eeezee!

All it take is to type-assert the true type of the value assigned to the interface. In other words, type-assert the type of x. With this “one weird trick that makes even CommonLisp diehards envious” (spoiler alert: I bet they don't even care), we can drill down to the value the interface holds and compare it against nil:

y.(*int) == nil

Bitte sehr! Run the code on the Go Playground, and try the same with a non-nil pointer.