This is a quick post to discuss an interesting bug that was recently unearthed by go vet
.
The following code is a simplified reduction of a larger piece of code. In the original code the if
statement was much larger, encompassing several complicated conditions, making the bug hard to spot visually.
package main import "fmt" import "io" type Thing struct { Reader_ io.Reader } func (t *Thing) Reader() io.Reader { return t.Reader_ } func main() { t := Thing{Reader_: nil} if t.Reader != nil { fmt.Println("wait a second") } }
Running this code gives the result
% go run thing.go wait a second
But … what is going on ? Thing.Reader_
is explicitly set to nil
(even though this is unnecessary, the zero value of an interface field is nil
), so how can the check for nil
on the very next line fail?
Let’s look at what go vet
thinks.
% go vet thing.go thing.go:14: comparison of function Reader != nil is always true exit status 1
The mistake in the original code was the author had intended to write t.Reader()
, but perhaps forgot the parenthesis. The uncommon use of the underscore suffix possibly contributed to the bug.
So, a quick fix and a code review later and the bug was closed. But, why was this code valid in the first place ? The answer is, since Go 1.1, the expression t.Reader
is no longer a syntax error, instead it evaluates to a Method Value.
Let’s look a little closer
t := Thing{Reader_: nil} fmt.Printf("Reader_: %T\n", t.Reader_) fmt.Printf("Reader(): %T\n", t.Reader()) fmt.Printf("Reader: %T\n", t.Reader)
gives the following output
Reader_: <nil> Reader(): <nil> Reader: func() io.Reader
The first line says t.Reader_
evaluates to nil
, as expected. t.Reader()
also evaluates to nil
. However, on the third line t.Reader
evaluates to a value whose type is func() io.Reader
, and as we see from the initial example, because this method value is derived from a method defined at compile time, it cannot beĀ nil
, so the comparison is always true.