Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

proposal: errors: add NewJoinedErrors #48831

Closed
goofinator opened this issue Oct 6, 2021 · 2 comments
Closed

proposal: errors: add NewJoinedErrors #48831

goofinator opened this issue Oct 6, 2021 · 2 comments

Comments

@goofinator
Copy link

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.

@ianlancetaylor ianlancetaylor changed the title proposal: errors: proposal: errors: add NewJoinedErrors Oct 6, 2021
@gopherbot gopherbot added this to the Proposal milestone Oct 6, 2021
@ianlancetaylor
Copy link
Contributor

There are various existing multierr packages, such as https://pkg.go.dev/go.uber.org/multierr. It's not clear why this needs to be in the standard library.

This may be a dup of #47811.

@rsc
Copy link
Contributor

rsc commented Oct 13, 2021

This proposal is a duplicate of a previously discussed proposal, as noted above,
and there is no significant new information to justify reopening the discussion.
The issue has therefore been declined as a duplicate.
— rsc for the proposal review group

@rsc rsc closed this as completed Oct 13, 2021
@rsc rsc moved this to Declined in Proposals Aug 10, 2022
@rsc rsc added this to Proposals Aug 10, 2022
@golang golang locked and limited conversation to collaborators Oct 13, 2022
@rsc rsc removed this from Proposals Oct 19, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

4 participants