-
Notifications
You must be signed in to change notification settings - Fork 18.4k
Description
Description
Sometimes I miss the capabilities of the standard library to handle errors. Wrapping an error in context with fmt.Errorf("some context: %w",err)
is a great feature. But there are situations where having multiple errors of the form
var errMyError1 = errors.New("my error 1")
var errMyError2 = errors.New("my error 2")
I need to pack them both into an error followed by the ability to detect this fact with
errors.Is(err, errMyError1)
errors.Is(err, errMyError2)
Example Implementation
At the moment, I'm using my own type to solve such problems:
type joinedErrors struct {
errExt error
errInt error
}
func (e joinedErrors) Error() string {
return fmt.Sprintf("%s: %s", e.errExt, e.errInt)
}
func (e joinedErrors) Unwrap() error {
return e.errInt
}
func (e joinedErrors) Is(target error) bool {
return errors.Is(e.errExt, target)
}
func (e joinedErrors) As(target interface{}) bool {
return errors.As(e.errExt, target)
}
Using the following function
func NewJoinedErrors(errExt error, errInt error) error {
return joinedErrors{errExt: errExt, errInt: errInt}
}
i can get what i want.
Example Using
someErr1 := errors.New("my error 1")
someErr2 := errors.New("my error 2")
err:=fmt.Errorf("error occured: %w", someErr1)
// errNegative1 := fmt.Errorf("%w: %w", someErr2, err) - will cause the error
// using the standard library
errNegative1 := fmt.Errorf("%w: %s", someErr2, err)
fmt.Printf("%q, %v, %v\n",errNegative1, errors.Is(errNegative1,someErr1), errors.Is(errNegative1,someErr2))
errNegative2 := fmt.Errorf("%s: %w", someErr2, err)
fmt.Printf("%q, %v, %v\n",errNegative2, errors.Is(errNegative2,someErr1), errors.Is(errNegative2,someErr2))
// using the NewJoinedErrors
errPositive := NewJoinedErrors(someErr2, err)
fmt.Printf("%q, %v, %v\n",errPositive, errors.Is(errPositive,someErr1), errors.Is(errPositive,someErr2))
You can try it here
Conclusion
I think a function like NewJoinedErrors might be useful for someone and should probably be included in the errors package.
The only drawback I see is some inconvenience in managing the text context. The context can be added for the overridden errors separately or later for the resulting error.
As a user, it would be more convenient for me to use fmt.Errorf("some context %w: and more context: %w", errExt, errInt)
notation instead of the proposed construct. In this case, the errExt, errInt ratio under the hood is specified by the order of variables, which may not be quite obvious (meaning which error will be retrieved first during Unwrap).
Another option is to add a specifier for external error: fmt.Errorf("some context %w: and more context: %W", errInt, errExt)
, so the user knows that errExt will be extracted first, though I cannot immediately imagine a situation where this really matters.
I believe that the above changes will not spoil backward compatibility and will give error handling a more generic look.