Skip to content

proposal: errors: Is resolve nil error problem #40674

Closed
@carnott-snap

Description

@carnott-snap

background

A long standing problem with error handling, but really checking nilness of interface variables, is typed nils:

var p *MyError = nil
if bad() {
	p = ErrBad
}
return p // Will always return a non-nil error.

description

Since we already introduced a function than handles error comparisons, errors.Is, we could resolve this edge case by checking both err == nil and underlying value nilness:

var err error = (*MyError)(nil)
switch {
case err == nil:
        // false
case errors.Is(err, nil):
        // true
}

costs

This will make errors.Is more complex, and meant that developers should eschew err != nil in favour of !errors.Is(err, nil). At the same time, we already suggest errors.Is(err, ErrBad) over err == ErrBad, so this would serve to make the language more consistent.

alternatives

It may also be confusing that this only applies for nilable types. We could instead define that all default value types are nil:

type MyError struct{}

func (MyError) Error() string { return "" }

func main() {
	_ = errors.Is(MyError{}, nil) // == true
}

Since every implementer may want to handle the nil case differently, we could instead upon the implementation of interface{ Is(error) bool } to determine what should be done. This is a larger topic so see #40673. That being said, for errors that are implemented upon types, e.g. func (MyError) Error() string, we may need to have a standard resolution for (*MyError)(nil). Furthermore, if #40673 is accepted, we should allow it to override any defaults suggested here.

Since we are flattening nilness in the err parameter, should we allow the same in the target, errors.Is(err, (*MyError)(nil))?

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions