Why is err != nil true here even though the function returned a nil pointer?
Question 488HardGo 1.22 to 1.25
type MyErr struct{ msg string }
func (e *MyErr) Error() string { return "myerr" }
func do(fail bool) error {
var p *MyErr
if fail {
p = &MyErr{"bad"}
}
return p // BUG
}
func main() {
err := do(false)
fmt.Println(err == nil) // false
fmt.Printf("%T %v\n", err, err) // *main.MyErr myerr
}
An interface value is a pair (dynamic type, dynamic value). It equals nil only when both are nil. Returning a typed nil pointer as error gives (*MyErr, nil). The type half is set, so the comparison is false. Callers then treat it as a failure, and a method that dereferences the receiver will panic.
Fix: return the literal nil on success and never pass a concrete error type through an error return:
func do(fail bool) error {
if fail {
return &MyErr{"bad"}
}
return nil
}
The same trap shows up with var r io.Reader = (*bytes.Buffer)(nil). To check for a typed nil you need reflection (reflect.ValueOf(x).IsNil()), which is a design smell. The interviewer wants the two-word model of interfaces and the rule "return interface types as untyped nil".
More on Tricky Output & Code-Review Puzzles
- Q486Array vs slice in range: what prints?
- Q487Why doesn't this loop update the users?
- Q489Why doesn't this compile, and why does Go refuse it?
- Q490When is the receiver bound for a method value? What prints?
- Q491Code review: why does Counter always report 0, and what else is wrong?
- Q492What happens if you add or delete map entries while ranging over the map?