Go

The nil error interface gotcha: what does this print?

Question 302HardGo 1.22 to 1.25
type MyErr struct{}

func (*MyErr) Error() string { return "my err" }

func do(fail bool) error {
	var p *MyErr // nil pointer
	if fail {
		p = &MyErr{}
	}
	return p // converts *MyErr to error
}

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

An interface value is a (type, value) pair, and it is nil only when both parts are nil. Returning a typed nil pointer produces (*MyErr, nil), a non-nil interface, so the caller treats success as failure. %v still prints "my err" because Error has a pointer receiver and never dereferences it.

Fixes: always return nil explicitly on success, and declare the function's result as error, never *MyErr. Don't store results in a concrete-typed variable before returning them as error.

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

staticcheck (SA4023) and nilaway can catch some cases. Interviewers ask this to check that you understand how interfaces are represented.

More on Error Handling & panics

All 37 Error Handling & panics questions