-
Notifications
You must be signed in to change notification settings - Fork 18k
proposal: errors: allow implementing err.Is(nil) == true #40673
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
How is this different from #40442? I don't think we should make any changes to the semantics of |
#40442 was filed as a bug, and I went into it with the impression that something was wrong. This is a proposal to change the current behaviour. This property is undocumented, and (to my knowledge) was not mentioned as (an essential) part of the Go2 error handling discussion. If this is important, we should document it. That being said, can you describe why you feel this is an important property? We will still have |
There is a great deal of existing Go code which uses |
This change would not invalidate that code, similar to how one is not required to move from
Sure, but I do not think this is something that most users care about. To me, the whole point of Reiterating the costs section, if this opinion is canonical, we need to update the documentation to make clear what is going on. But speaking to @ianlancetaylor's comment on a different issue, #39799, it seems problematic that we have two ways to do one thing. |
It sounds like you are suggesting that there should be cases where |
That is what I am suggesting, but to me this is very intuitive, since This is not the implementation, but I always thought about |
@carnott-snap Today we have a world in which a function either returns an error ( |
You seem to be suggesting that we cannot migrate to away from equality checks. This is inaccurate for new apis. If I document that my api requires go1.13+ and returned Also, your world view seems to be missing a step:
Depending on how the compatibility guarantee is read, I would be understanding if we needed to deprecate |
That isn't the case. There is tons of Go code that correctly says I agree that new APIs could document that callers must test errors using err := newapi.F()
if errors.Is(err, nil) {
err = nil
}
return err I am honestly struggling to imagine what possible benefit we could see from Again, this suggestion might be plausible if we didn't have a large amount of existing code. But we can't ignore that code, we can't ignore the switching costs, we can't ignore the interoperability costs. We need an extraordinary benefit to be worth paying the cost. I don't see that benefit. Every change has a cost. We can't judge changes solely by the benefit they bring. We must compare the benefit to the cost. |
I agree that for extant interfaces, they would need to keep whatever compatibility promise they offer today, but this is like with the inclusion of A new paradigm may introduce a desire for adaptor packages, but there is no requirement. If we considered this a large enough population, we (or a third party library) could write an adaptor function: package errors
func Old(err error) error {
if !errors.Is(err, nil) {
return err
}
return nil
} And your example becomes: err := errors.Old(newapi.F()) And if you need to add functionality from the new style to an old api, the code reads identically to func old() error {
v, err := new()
if !errors.Is(err, nil) {
return err // or wrapped fmt.Errorf("old to new: %w", err)
}
_ = v // consume v
return nil
} I will not suggest this has no costs, and respect that we are implicitly making most of the ecosystem's error handling code an anti-pattern. However there is no interoperability issue, because we do not break any existing code. When you write new code, it will look different, and if you want to migrate, that is each library owner's choice, much like |
We aren't going to make almost all existing error handling code an anti-pattern. No benefit we can realize here will be worth that cost. |
But we already did that for |
That is not what we did. In the past, it only made sense to write What we changed was to add a new capability: some functions may now explicitly define the API as returning an error that wraps some other error, typically be providing additional information. This isn't going to be changed for code that already specifies that it returns The fact that we introduced In other words, |
While this nuance may be accurate, it is not well captured in documentation or (community) linters. Many linters outright require use of
Is this not worse than migrating wholesale to
Can this change not be staged in the same way? I will admit I assumed that |
Hmmm. If linters are suggesting that code change I don't think of
For almost all code there is just one way to check errors: It's true that we could say "everybody stop writing
If people really want to write |
Just for your visibility, there are three linters that I have seen/used that have this property:
This nuance is lost on new users. When I read this blog post, I got the strong impression that because
To someone who has been using
Is it really any more confusing than |
The only possible meaning I can think of for Thanks for your comments about optics and about those linters. Still, I'm confident that those linters don't warn about |
Based on the conversation above, this seems like a likely decline. |
Correct, they suggest
This is not a breaking change. |
Telling everyone that all their code using err == nil is now incorrect and must be updated to use errors.Is(err, nil) is a breaking change. We can't rewrite all the code in the world. |
As long as old code continues to compile and run, a change is not breaking. It is not a breaking change to deprecate a design pattern. |
No change in consensus, so declined. |
background
Currently you can implement,
func (MyError) Is(target) bool { return true }
, butMyError.Is
will only be called iftarget
, inerrors.Is
, is non-nil
. This can be confusing to implementers ofinterface{ Is(error) bool }
, and disallows signalling that a given returned error isnil
. (This is useful when you want to return error metadata, but nothing actually failed.)description
I propose removing the
target == nil
check fromerrors.Is
. This will hand full control of equality checking tointerface { Is(error) bool }
, and make the user experience more consistent.costs
It is my understanding that we do not currently advocate for
!errors.Is(err, nil)
overerr != nil
, so the impact of this should be limited. Furthermore, if you have implementedfunc (MyError) Is(error) bool { return true }
, you are either relying upon undefined behaviour or have a bug. That being said, this is a semantic change, so we should be careful to understand what the impact is.alternatives
If the current implementation is preferred, it should be explicitly documented.
Is reports whether any error in err's chain matches target
, does not describe that fortarget == nil
MyTarget.Isonly equality, and never
MyError.Is`, will checked.The text was updated successfully, but these errors were encountered: