77[ ![ Go Report Card] ( https://goreportcard.com/badge/fillmore-labs.com/exp/errors )] ( https://goreportcard.com/report/fillmore-labs.com/exp/errors )
88[ ![ License] ( https://img.shields.io/github/license/fillmore-labs/errors-exp )] ( https://www.apache.org/licenses/LICENSE-2.0 )
99
10- ` fillmore-labs.com/exp/errors ` is an experimental Go library that provides two enhanced, generic alternatives to
10+ ` fillmore-labs.com/exp/errors ` is an experimental Go library that provides four enhanced, generic alternatives to
1111` errors.As ` for inspecting error chains with improved ergonomics and type safety.
1212
1313## Motivation
@@ -21,17 +21,19 @@ linter.
2121
2222## Function Overview
2323
24- This library provides two complementary functions :
24+ This library provides four functions in two complementary pairs, each building on Go's standard ` errors.As ` :
2525
26- - ** ` HasError ` ** - A drop-in, type-safe replacement for ` errors.As ` with better ergonomics
27- - ** ` Has ` ** - An enhanced version that also handles pointer-value type mismatches automatically
26+ - ** ` HasError ` ** - Ergonomic and type-safe, returns the found error.
27+ - ** ` Has ` ** - The ` HasError ` API plus pointer-value mismatch handling.
2828
29- | Feature | ` errors.As ` | ` HasError ` | ` Has ` |
30- | ------------------------------- | ----------- | ---------- | ----- |
31- | Generic return type | ❌ | ✅ | ✅ |
32- | No target variable needed | ❌ | ✅ | ✅ |
33- | Interface support | ✅ | ✅ | ✅ |
34- | Pointer-value mismatch handling | ❌ | ❌ | ✅ |
29+ - ** ` AsError ` ** - The classic ` errors.As ` API with generic type safety.
30+ - ** ` As ` ** - The classic ` errors.As ` API plus pointer-value mismatch handling.
31+
32+ | Feature | ` errors.As ` | ` HasError ` | ` Has ` | ` AsError ` | ` As ` |
33+ | ------------------------------- | ----------- | ---------- | ----- | --------- | ---- |
34+ | No target variable needed | ❌ | ✅ | ✅ | ❌ | ❌ |
35+ | Pointer-value mismatch handling | ❌ | ❌ | ✅ | ❌ | ✅ |
36+ | Type safe generics | ❌ | ✅ | ✅ | ✅ | ✅ |
3537
3638## ` HasError ` - Enhanced Ergonomics
3739
@@ -128,21 +130,23 @@ func Has[T error](err error) (T, bool)
128130
129131#### Prevents Common Bugs
130132
131- This mismatch would silently fail with ` errors.As ` :
133+ This mismatch would silently fail with ` errors.As ` . In the example below, ` aes.NewCipher ` returns an ` aes.KeySizeError `
134+ value, but the check incorrectly looks for a pointer (` *aes.KeySizeError ` ):
132135
133136``` go
134137 key := []byte (" My kung fu is better than yours" )
135138 _ , err := aes.NewCipher (key)
136139
137- // With errors.As - this check fails silently.
140+ // With errors.As, this check for a pointer type fails because the
141+ // actual error is a value.
138142 var kse *aes.KeySizeError
139143 if errors.As (err, &kse) {
140- fmt. Printf ( " Wrong AES key size: %d bytes. \n " , *kse)
144+ // This code is never reached.
141145 }
142146
143- // With Has - the check succeeds.
147+ // With Has, the check succeeds because it handles the pointer-value mismatch .
144148 if kse , ok := Has [*aes.KeySizeError ](err); ok {
145- fmt.Printf (" AES keys must be 16, 24, or 32 bytes long, got %d bytes.\n " , *kse)
149+ fmt.Printf (" Invalid AES key size: %d bytes. Key must be 16, 24, or 32 bytes.\n " , *kse)
146150 }
147151```
148152
@@ -166,6 +170,44 @@ Unlike `errors.As`, interface types provided to `Has` or `HasError` must embed t
166170 if temp , ok := Has [interface { error ; Temporary () bool }](err); ok && temp.Temporary () { /* handle temporary error */ }
167171```
168172
173+ ## Classic API with Enhanced Safety
174+
175+ If you prefer the traditional ` errors.As ` API that uses target variables, this library provides enhanced versions that
176+ add the same benefits:
177+
178+ ### ` AsError ` - Type-Safe ` errors.As `
179+
180+ ` AsError ` provides generic type safety to prevent target variable type mismatches:
181+
182+ ``` go
183+ func AsError [T error ](err error , target *T) bool
184+
185+ // Usage
186+ var myErr *MyError
187+ if AsError (err, &myErr) {
188+ // myErr is guaranteed to be the correct type
189+ // No risk of type assertion bugs
190+ }
191+ ```
192+
193+ ### ` As ` - Type-Safe with Flexible Matching
194+
195+ ` As ` combines type safety with the same pointer-value mismatch handling as ` Has ` :
196+
197+ ``` go
198+ func As [T error ](err error , target *T) bool
199+
200+ // Usage
201+ var myErr *MyError
202+ if As (err, &myErr) {
203+ // Also handles pointer-value mismatches automatically
204+ // Most robust option with familiar API
205+ }
206+ ```
207+
208+ Both functions prevent common type-related bugs while maintaining the familiar ` errors.As ` API that some developers
209+ prefer.
210+
169211## Migration Guide
170212
171213### From ` errors.As ` to ` HasError `
@@ -191,6 +233,13 @@ If you suspect pointer-value mismatches are causing issues, replace `HasError` w
191233 if myErr , ok := Has [MyError](err); ok { /* ... */ }
192234```
193235
236+ ## Further Reading
237+
238+ - Blog: [ Understanding Go Error Types: Pointer vs. Value] ( https://blog.fillmore-labs.com/posts/errors-1/ ) - Background
239+ on pointer-value type mismatches
240+ - [ Go proposal: errors: As with type parameters] ( http://go.dev/issues/51945 ) - Community discussion on improving
241+ ` errors.As ` with generics
242+
194243## License
195244
196245This project is licensed under the Apache License 2.0. See the [ LICENSE] ( LICENSE ) file for details.
0 commit comments