Go

Why is an interface holding a nil pointer not equal to nil? What does this print?

Question 31HardGo 1.22 to 1.25
type MyErr struct{}

func (*MyErr) Error() string { return "boom" }

func do(fail bool) error {
	var p *MyErr // nil pointer
	if fail {
		p = &MyErr{}
	}
	return p // BUG: always returns a non-nil interface
}

func main() {
	err := do(false)
	fmt.Println(err == nil)       // false
	fmt.Printf("%T %v\n", err, err) // *main.MyErr boom
}

An interface value is a pair: (dynamic type, dynamic value). It equals nil only if both parts are nil. Returning a typed nil pointer produces (*MyErr, nil). Its type is set, so the interface is not nil, and err != nil is true. Calling err.Error() still works here, because the method doesn't dereference its receiver.

The fix is to return the literal nil for the interface. Don't let a concrete pointer type reach an interface return value:

func do(fail bool) error {
	if fail {
		return &MyErr{}
	}
	return nil
}

To check whether the value inside is nil, use reflect.ValueOf(err).IsNil(), but only for kinds that can be nil. What the interviewer wants to hear: an explanation of the two-word interface layout (itab/_type plus a data pointer), and the rule that functions should return the error interface, never a concrete error pointer type. Note that reflect.ValueOf(err).IsNil() itself panics when err is a true nil interface, so check err != nil first. No default go vet check catches the typed-nil return. Staticcheck's SA4023 flags some comparisons of this kind that can never be true, but code review is the main defence.

More on Language Fundamentals & Types

All 39 Language Fundamentals & Types questions