go: proposal: errors: As with type parameters
Currently in 1.18 and before, when using the errors.As method, an error type you would like to write into must be predeclared before calling the function. For example:
var myErr *MyCustomError
if errors.As(err, &myErr) {
// handle myErr
}
This can makes control flow around handling errors “unergonomic”.
I’d propose that a new, type parameterized method be added to the errors package in 1.19:
func IsA[T error](err error) (T, bool) {
var isErr T
if errors.As(err, &isErr) {
return isErr, true
}
var zero T
return zero, false
}
This enables more “ergonomic” usage as follows:
err := foo()
if err != nil {
if myErr, ok := errors.IsA[*MyCustomError](err); ok {
// handle myErr
} else if otherErr, ok := errors.IsA[*OtherError](err); ok {
// handle otherErr
}
// handle everything else
}
instead of
err := foo()
if err != nil {
var myErr *MyCustomError
if errors.As(err, &myErr) {
// handle customer error
}
var otherErr *OtherError
if errors.As(err, &otherErr) {
// handle other error
}
// handle everything else
}
This change would reduce the overall LOC needed for handling custom errors, imo improves readability of the function, as well as scopes the errors to the if blocks they are needed in.
Naming is hard so IsA might be better replaced with something else.
About this issue
- Original URL
- State: open
- Created 2 years ago
- Reactions: 29
- Comments: 22 (10 by maintainers)
That’s not quite true: This does add type safety at the language level, rather than leaving it to a
go vetcheck. Under this proposal, this code would not be valid:The equivalent
errors.Ascall is a run-time panic orgo vetfailure:There are a few benefits of having a generic version of
As, that I don’t think have been brought up here. I mentioned them in: https://github.com/golang/go/issues/56949User @Jorropo wrote the function this way:
Note that it does not use the current
Asimplementation. The benefits of this implementation are:This would break the following:
@rsc made the case in the original proposal issue that a type-parameterized version of
errors.Aswould not be an improvement: https://github.com/golang/go/issues/29934#issuecomment-490091428@dsnet also pointed out that the non-type-parameterized version of
Ascan be used in a switch, while the type-parameterized one cannot: https://github.com/golang/go/issues/29934#issuecomment-460093518I don’t recall if there were any other arguments against a type-parameterized
As(aside from, obviously, the fact that type parameters didn’t exist at the time).I personally think that a type-parameterized
Asseems fairly reasonable, although the practical benefit over the currentAsseems small. It would need a good name. I don’t know what that name would be.IsAdoes not seem right. Under the current design, “Is” is an enhanced form of equality and “As” is an enhanced form of type assertion; blurring that distinction would add confusion.An argument against adding a type-parameterized
Asis that the benefit does not justify the cost in API churn and user confusion. I don’t have a strong opinion on whether the benefits do outweigh the costs.#64629 was closed as duplicate, so let me advertise my idea here. Perhaps, instead of adding new
IsAfunction, it would be easier to add something that actually produces that double pointer? Like this:Then we could:
Love this, I stumbled across this pattern, made a quick blog post about it. Then, of course, that is when I find all the proposals 😂.
I think you can get it down to this terse definition:
Per (closed) duplicate proposal #64771, I advocate for the name
errors.Has. Implementation would look like:CC @jba @neild
(My vague recollection is that this was considered and rejected when
errors.Aswas introduced, even beyond the fact that at the time type parameters did not yet exist.)A) It’s an Is check, not really an As check, which is less useful. B) That would be very prone to accidental misuse in which the type system would infer
errorinstead of a concrete error type.Darn, you are right. I missed that case. Kinda sucks though, because the change would absolutely help with bugs that aren’t discovered until testing or some other runtime occurrence.
All of these proposals are looking for a way to return the error as a specific type, which I agree is nice if possible, but have there been any discussions around simply improving
errors.Aswith generics? Specifically:At first glance this appears to be a breaking change, but passing anything that doesn’t meet this criteria into the
Asfunction would result in a panic. This change would alert people of the issue sooner (during compile time) rather than at runtime.It is also very possible I am missing an edge case.
I have looked, but haven’t found an issue that discusses this approach. Please let me know if one exists.