4

proposal: errors: add support for wrapping multiple errors · Issue #53435 · gola...

 1 year ago
source link: https://github.com/golang/go/issues/53435
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Contributor

neild commented on Jun 17

edited

For the most recent version of this proposal, see: #53435 (comment) below.


This is a variation on the rejected proposal #47811 (and perhaps that proposal should just be reopened), and an expansion on a comment in it.

Background

Since Go 1.13, an error may wrap another by providing an Unwrap method returning the wrapped error. The errors.Is and errors.As functions operate on chains of wrapped errors.

A common request is for a way to combine a list of errors into a single error.

Proposal

An error wraps multiple errors if its type has the method

Unwrap() []error

Reusing the name Unwrap avoids ambiguity with the existing singular Unwrap method. Returning a 0-length list from Unwrap means the error doesn't wrap anything. Callers must not modify the list returned by Unwrap. The list returned by Unwrap must not contain any nil errors.

We replace the term "error chain" with "error tree".

The errors.Is and errors.As functions are updated to unwrap multiple errors. Is reports a match if any error in the tree matches. As finds the first matching error in a preorder traversal of the tree.

The errors.Join function provides a simple implementation of a multierr. It does not flatten errors.

// Join returns an error that wraps the given errors.
// Any nil error values are discarded.
// The error formats as the text of the given errors, separated by sep.
// Join returns nil if errs contains no non-nil values.
func Join(sep string, errs ...error) error

The fmt.Errorf function permits multiple instances of the %w formatting verb.

The errors.Split function retrieves the original errors from a combined error.

// Split returns the result of calling the Unwrap method on err,
// if err's type contains an Unwrap method returning []error.
// Otherwise, Split returns nil.
func Split(err error) []error

The errors.Unwrap function is unaffected: It returns nil when called on an error with an Unwrap() []error method.

Questions

Prior proposals have been declined on the grounds that this functionality can be implemented outside the standard library, and there was no good single answer to several important questions.

Why should this be in the standard library?

This proposal adds something which cannot be provided outside the standard library: Direct support for error trees in errors.Is and errors.As. Existing combining errors operate by providing Is and As methods which inspect the contained errors, requiring each implementation to duplicate this logic, possibly in incompatible ways. This is best handled in errors.Is and errors.As, for the same reason those functions handle singular unwrapping.

In addition, this proposal provides a common method for the ecosystem to use to represent combined errors, permitting interoperation between third-party implementations.

How are multiple errors formatted?

A principle of the errors package is that error formatting is up to the user. This proposal upholds that principle: The errors.Join function combines error text with a user-provided separator, and fmt.Errorf wraps multiple errors in a user-defined layout. If users have other formatting requirements, they can still create their own error implementations.

How do Is and As interact with combined errors?

Every major multierror package that I looked at (see "Prior art" below) implements the same behavior for Is and As: Is reports true if any error in the combined error matches, and As returns the first matching error. This proposal follows common practice.

Does creating a combined error flatten combined errors in the input?

The errors.Join function does not flatten errors. This is simple and comprehensible. Third-party packages can easily provide flattening if desired.

Should Split unwrap errors that wrap a single error?

The errors.Split function could call the single-wrapping Unwrap() error method when present, converting a non-nil result into a single-element slice. This would allow traversing an error tree with only calls to Split.

This might allow for a small improvement in the convenience of code which manually traverses an error tree, but it is rare for programs to manually traverse error chains today. Keeping Split as the inverse of Join is simpler.

Why does the name of the Split function not match the Unwrap method it calls?

Giving the single- and multiple-error wrapping methods the same name neatly avoids any questions of how to handle errors that implement both.

Split is a natural name for the function that undoes a Join.

While we could call the method Split, or the function UnwrapMultiple, or some variation on these options, the benefits of the above points outweigh the value in aligning the method name with the function name.

Prior art

There have been several previous proposals to add some form of combining error, including:

https://go.dev/issue/47811: add Errors as a standard way to represent multiple errors as a single error
https://go.dev/issue/48831: add NewJoinedErrors
https://go.dev/issue/20984: composite errors
https://go.dev/issue/52607: add With(err, other error) error
fmt.Errorf("%w: %w", err1, err2) is largely equivalent to With(err1, err2).

Credit to @jimmyfrasche for suggesting the method name Unwrap.

There are many implementations of combining errors in the world, including:

https://pkg.go.dev/github.com/hashicorp/go-multierror (8720 imports)
https://pkg.go.dev/go.uber.org/multierr (1513 imports)
https://pkg.go.dev/tailscale.com/util/multierr (2 imports)

jimmyfrasche, DeedleFake, ix64, qmuntal, tdakkota, kushuh, maja42, marco-m, josharian, richjddavis, and 18 more reacted with thumbs up emojirittneje, elagergren-spideroak, and ericlagergren reacted with thumbs down emojicarlmjohnson, kushuh, josharian, florianl, and meling reacted with heart emojiseptemhill and seankhliao reacted with eyes emoji All reactions

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK