Go errors.Is
now includes a nil check.
Summary
In Go 1.23, the addition of a nil check to errors.Is
makes it easier to maintain the happy path when handling errors.
After the Go 1.23 freeze period, the go1.23rc
tag has finally been released.
Here are the release notes.
However, there is a section that I personally consider important, which is missing. It concerns the errors.Is
.
Before Go 1.23, errors.Is
did not check if the error being examined was nil. The code at that time was as follows:
func Is(err, target error) bool {
if target == nil {
return err == target
}
isComparable := reflectlite.TypeOf(target).Comparable()
return is(err, target, isComparable)
}
Therefore, to check if the error returned by a function was a specific error, you had to use nested if statements like this:
err := foo()
if err != nil {
if errors.Is(err, errorBar) {
// handle error Bar
}
if errors.Is(err, errorBar2) {
// handle error Bar2
}
// handle other errors
}
While this code might not cause any issues, it is not particularly pretty.
With Go 1.23, you can now handle error checking with only a single level of depth, eliminating the need for nested if statements. This improvement is due to the following code change:
func Is(err, target error) bool {
if err == nil || target == nil {
return err == target
}
isComparable := reflectlite.TypeOf(target).Comparable()
return is(err, target, isComparable)
}
Previously, the errors.Is
only checked if the target
error (i.e., the predefined error) was nil. However, now it also checks if the actual error returned is nil.
As a result, we can improve the code as follows:
err := foo()
if errors.Is(err, errorBar) {
// handle error Bar
}
if errors.Is(err, errorBar2) {
// handle error Bar2
}
if err != nil {
// handle other errors
}
I believe this approach is better than the previous code. Although it might seem like a small change, it significantly improves readability and reduces complexity compared to the old one.
The previous code, with its nested if statements, can be more confusing and complex to read later on, despite appearing straightforward when initially written.
When writing code, it’s essential to handle other errors, but there’s also a risk of missing non-exception errors while managing exceptions.
Code without unnecessary depth like this is defined as satisfying the HAPPY PATH.
In the book 100 Go Mistakes and How to Avoid Them, the author states: “Avoiding nested levels and keeping the happy path aligned on the left makes building a mental code model easier.” And I completely agree with that.
Therefore, in the future, let’s make sure to follow the happy path approach as shown when handling errors.
Conclusion
When errors.Is
did not have a nil check, I encountered numerous mistakes and difficulties in reading code, and I was quite dissatisfied with the lack of a nil check in errors.Is
.
However, now that it has been introduced, and despite the philosophical disagreements some Gophers might have, this enhancement will significantly aid in writing code that maintains the happy path, improving readability and reducing complexity.