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

make Error compatible with functions in standard package #35

Merged
merged 3 commits into from
May 13, 2021
Merged

make Error compatible with functions in standard package #35

merged 3 commits into from
May 13, 2021

Conversation

disksing
Copy link

Signed-off-by: disksing i@disksing.com

The current Equals is not friendly to multi-level wrap errors: it always unwraps the bottom-level error and then compares them.

For example, if the bottom level returns os.NotExists, we cannot wrap it with kv.ErrNotExists.Wrap(err), because if we did that, we would not able to determine it later with kv.ErrNotExists.Equals(err) (the reason is Equals will recursively unwraps out os.NotExists then compare)

Now we deal this problem by throwing away the underlying error and just return kv.ErrNotExists, which is undoubtedly inappropriate because some information is lost and there is no way to tell if the error is os.ErrorNotExists anymore.

Go1.13 introduced the error wrapping mechanism in the standard package (https://golang.org/doc/go1.13#error_wrapping) and provides errors.Is / errors.As functions for error determination.

This PR adds two methods to make Error compatible with standard error wrapping.

  • Unwrap() returns the next level of the error chain without recursion, so that Is/As can be checked for each level.
  • Is() is used to ensure that all instances of Error with the same ID are judged to be equal.

There are 2 cases need to be noted:

e1 := fooError{}
e2 := Normalize("e2", RFCCodeText("e2"))
e21 := e2.Wrap(e1)

Since the ID of both e2 and e21 are "e2", so errors.Is(e21, e2) and errors.Is(e2, e21) are both true.

e1 := fooError{}
e2 := Normalize("e2", RFCCodeText("e2"))
e3 := Normalize("e3", RFCCodeText("e3"))
e21 := e2.Wrap(e1)

e3x := e3.Wrap(e1)
ok := errors.As(e21, &e3x)

In this case, although e3x and e21 have different IDs, the are of the same type (both are *Error), so As returns true and e3x.ID() == "e2" afterward. Due to limitation of errors.As, this issue cannot be avoided by implementing an As function for Error.

Copy link
Collaborator

@lysu lysu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@lysu lysu requested a review from YuJuncen April 21, 2021 09:35
Copy link

@YuJuncen YuJuncen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@disksing
Copy link
Author

Sorry, I found out that this PR may be somehow incomplete now. Please DNM until I get it clear.

@disksing disksing changed the title make Error compatible with functions in standard package [DNM] make Error compatible with functions in standard package Apr 22, 2021
Signed-off-by: disksing <i@disksing.com>
Signed-off-by: disksing <i@disksing.com>
Signed-off-by: disksing <i@disksing.com>
@disksing disksing changed the title [DNM] make Error compatible with functions in standard package make Error compatible with functions in standard package Apr 28, 2021
@disksing
Copy link
Author

@lysu
Copy link
Collaborator

lysu commented May 6, 2021

LGTM

@disksing
Copy link
Author

@lysu @YuJuncen could we merge it now? it should have no side effects.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants