gotchagoMajor
nil interface vs nil pointer: interface equality trap
Viewed 0 times
nil interfacenil pointerinterface equalitytyped nilerror interfacenon-nil interface nil pointer
Error Messages
Problem
A non-nil interface holding a nil pointer is NOT equal to nil. This causes surprising behaviour when returning concrete error types wrapped in an interface.
Solution
Always return a plain nil for interface return values, not a typed nil:
type MyError struct{ msg string }
func (e *MyError) Error() string { return e.msg }
// WRONG — returns a non-nil interface containing a nil *MyError
func doWork() error {
var err *MyError
if somethingFailed {
err = &MyError{"oops"}
}
return err // even when nil, interface is NOT nil
}
// RIGHT — return nil directly
func doWork() error {
if somethingFailed {
return &MyError{"oops"}
}
return nil
}
// Check:
err := doWork()
if err != nil { // may be true even if *MyError is nil!Why
An interface value is a pair of (type pointer, value pointer). A nil interface has both as nil. But a nil *MyError stored in an error interface has a non-nil type pointer, so the interface itself is non-nil.
Gotchas
- This is the most common source of 'nil pointer dereference' on an apparently nil error
- Returning concrete types from functions that return interfaces is the root cause — avoid it
- reflect.ValueOf(err).IsNil() will correctly detect the nil pointer inside the interface
Revisions (0)
No revisions yet.