- Notifications
You must be signed in to change notification settings - Fork0
fillmore-labs.com/exp/errors is an experimental Go library providing two enhanced, generic alternatives to errors.As for inspecting error chains with improved ergonomics and type safety.
License
fillmore-labs/errors-exp
Folders and files
| Name | Name | Last commit message | Last commit date | |
|---|---|---|---|---|
Repository files navigation
fillmore-labs.com/exp/errors is an experimental Go library that provides four enhanced, generic alternatives toerrors.As for inspecting error chains with improved ergonomics and type safety.
In Go, error types can be designed for use as either values or pointers. When inspecting an error chain, a mismatchbetween the type you're looking for (e.g.,MyError) and the type that was actually wrapped (e.g.,*MyError) can leadto subtle, hard-to-find bugs where errors are missed.
Seethis blog post to read more about the issue and theerrortypelinter.
This library provides four functions in two complementary pairs, each building on Go's standarderrors.As:
HasError- Ergonomic and type-safe, returns the found error.Has- TheHasErrorAPI plus pointer-value mismatch handling.AsError- The classicerrors.AsAPI with generic type safety.As- The classicerrors.AsAPI plus pointer-value mismatch handling.
| Feature | errors.As | HasError | Has | AsError | As |
|---|---|---|---|---|---|
| No target variable needed | ❌ | ✅ | ✅ | ❌ | ❌ |
| Pointer-value mismatch handling | ❌ | ❌ | ✅ | ❌ | ✅ |
| Type safe generics | ❌ | ✅ | ✅ | ✅ | ✅ |
HasError is a generic, type-safe replacement forerrors.As that offers improved ergonomics and readability.
funcHasError[Terror](errerror) (T,bool)
Witherrors.As, you must declare a variable beforehand:
varmyErr*MyErroriferrors.As(err,&myErr) {/* ... use myErr */ }
HasError allows you to declare and check in one line:
ifmyErr,ok:=HasError[*MyError](err);ok {/* ... use myErr */ }
The syntax forerrors.As can sometimes obscure intent, especially when you only need to check for an error's presencewithout using its value. Note howerrors.As requires apointer to a struct literal to check for avalue type:
// This check is valid, but not immediately clear.iferrors.As(err,&x509.UnknownAuthorityError{}) {/* ... */ }
HasError makes the intent explicit and is easier to read:
// The type is clearly specified as a generic parameter.if_,ok:=HasError[x509.UnknownAuthorityError](err);ok {/* ... */ }
HasError works seamlessly with interfaces. To check if an error implements a specific interface:
// Single-line variantife,ok:=HasError[interface {error;Temporary()bool }](err);ok&&e.Temporary() {/* handle temporary error */ }// Or, using a named interface:typetemporaryinterface {error;Temporary()bool }ife,ok:=HasError[temporary](err);ok&&e.Temporary() {/* handle temporary error */ }
ChooseHasError when you need:
- Improved ergonomics compared to
errors.As. - Strict type matching, where pointers and values are treated as distinct types.
- To check if an error in the chainimplements a specific interface.
Has provides all the benefits ofHasError plus automatic handling of pointer-value type mismatches, preventingsubtle bugs.
funcHas[Terror](errerror) (T,bool)
Has automatically resolves pointer-value mismatches, finding errors thaterrors.As would miss:
// Scenario 1: Looking for a value (MyError), but a pointer (*MyError) was wrapped.err:=&MyError{msg:"oops"}ifmyErr,ok:=Has[MyError](err);ok {/* This matches! */ }// Scenario 2: Looking for a pointer (*MyError), but a value (MyError) was wrapped.err2:=MyError{msg:"oops"}ifmyErr,ok:=Has[*MyError](err2);ok {/* This also matches! */ }
This mismatch would silently fail witherrors.As. In the example below,aes.NewCipher returns anaes.KeySizeErrorvalue, but the check incorrectly looks for a pointer (*aes.KeySizeError):
key:= []byte("My kung fu is better than yours")_,err:=aes.NewCipher(key)// With errors.As, this check for a pointer type fails because the// actual error is a value.varkse*aes.KeySizeErroriferrors.As(err,&kse) {// This code is never reached. }// With Has, the check succeeds because it handles the pointer-value mismatch.ifkse,ok:=Has[*aes.KeySizeError](err);ok {fmt.Printf("Invalid AES key size: %d bytes. Key must be 16, 24, or 32 bytes.\n",*kse) }
ChooseHas when you want:
- The most robust error detection, with all the ergonomic benefits of
HasError. - Automatic handling of pointer-value mismatches to prevent subtle bugs caused by inconsistent error wrapping.
Unlikeerrors.As, interface types provided toHas orHasError must embed theerror interface.
// This is valid with errors.As:vartempinterface{Temporary()bool }iferrors.As(err,&temp)&&temp.Temporary() {/* handle temporary error */ }// With Has or HasError, the interface must embed `error`:iftemp,ok:=Has[interface {error;Temporary()bool }](err);ok&&temp.Temporary() {/* handle temporary error */ }
If you prefer the traditionalerrors.As API that uses target variables, this library provides enhanced versions thatadd the same benefits:
AsError provides generic type safety to prevent target variable type mismatches:
funcAsError[Terror](errerror,target*T)bool// UsagevarmyErr*MyErrorifAsError(err,&myErr) {// myErr is guaranteed to be the correct type// No risk of type assertion bugs }
As combines type safety with the same pointer-value mismatch handling asHas:
funcAs[Terror](errerror,target*T)bool// UsagevarmyErr*MyErrorifAs(err,&myErr) {// Also handles pointer-value mismatches automatically// Most robust option with familiar API }
Both functions prevent common type-related bugs while maintaining the familiarerrors.As API that some developersprefer.
// BeforevarmyErr*MyErroriferrors.As(err,&myErr) {returnfmt.Errorf("unexpected MyError: %w",myErr) }// AfterifmyErr,ok:=HasError[*MyError](err);ok {returnfmt.Errorf("unexpected MyError: %w",myErr) }
If you suspect pointer-value mismatches are causing issues, replaceHasError withHas.
// If this check sometimes fails unexpectedly:ifmyErr,ok:=HasError[MyError](err);ok {/* ... */ }// Switch to Has for more robust matching:ifmyErr,ok:=Has[MyError](err);ok {/* ... */ }
- Blog:Understanding Go Error Types: Pointer vs. Value - Backgroundon pointer-value type mismatches
- Go proposal: errors: As with type parameters - Community discussion on improving
errors.Aswith generics
This project is licensed under the Apache License 2.0. See theLICENSE file for details.
About
fillmore-labs.com/exp/errors is an experimental Go library providing two enhanced, generic alternatives to errors.As for inspecting error chains with improved ergonomics and type safety.
Topics
Resources
License
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Uh oh!
There was an error while loading.Please reload this page.