Accidental method value

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.