cockroachdb / errors

Go error library with error portability over the network

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

errors.IsAny does not support multi-error

intercept6 opened this issue · comments

It appears that errors.IsAny does not support multi-error.

errors/markers/markers.go

Lines 152 to 205 in c1cc191

func IsAny(err error, references ...error) bool {
if err == nil {
for _, refErr := range references {
if refErr == nil {
return true
}
}
// The mark-based comparison below will never match anything if
// the error is nil, so don't bother with computing the marks in
// that case. This avoids the computational expense of computing
// the reference marks upfront.
return false
}
// First try using direct reference comparison.
for c := err; c != nil; c = errbase.UnwrapOnce(c) {
for _, refErr := range references {
if refErr == nil {
continue
}
isComparable := reflect.TypeOf(refErr).Comparable()
if isComparable && c == refErr {
return true
}
// Compatibility with std go errors: if the error object itself
// implements Is(), try to use that.
if tryDelegateToIsMethod(c, refErr) {
return true
}
}
}
// Try harder with marks.
// Note: there is a more effective recursive algorithm that ensures
// that any pair of string only gets compared once. Should this
// become a performance bottleneck, that algorithm can be considered
// instead.
refMarks := make([]errorMark, 0, len(references))
for _, refErr := range references {
if refErr == nil {
continue
}
refMarks = append(refMarks, getMark(refErr))
}
for c := err; c != nil; c = errbase.UnwrapOnce(c) {
errMark := getMark(c)
for _, refMark := range refMarks {
if equalMarks(errMark, refMark) {
return true
}
}
}
return false
}

errors.Is supports multi-error.

func Is(err, reference error) bool {
if reference == nil {
return err == nil
}
isComparable := reflect.TypeOf(reference).Comparable()
// Direct reference comparison is the fastest, and most
// likely to be true, so do this first.
for c := err; c != nil; c = errbase.UnwrapOnce(c) {
if isComparable && c == reference {
return true
}
// Compatibility with std go errors: if the error object itself
// implements Is(), try to use that.
if tryDelegateToIsMethod(c, reference) {
return true
}
// Recursively try multi-error causes, if applicable.
for _, me := range errbase.UnwrapMulti(c) {
if Is(me, reference) {
return true
}
}
}
if err == nil {
// Err is nil and reference is non-nil, so it cannot match. We
// want to short-circuit the loop below in this case, otherwise
// we're paying the expense of getMark() without need.
return false
}
// Not directly equal. Try harder, using error marks. We don't do
// this during the loop above as it may be more expensive.
//
// Note: there is a more effective recursive algorithm that ensures
// that any pair of string only gets compared once. Should the
// following code become a performance bottleneck, that algorithm
// can be considered instead.
refMark := getMark(reference)
for c := err; c != nil; c = errbase.UnwrapOnce(c) {
if equalMarks(getMark(c), refMark) {
return true
}
}
return false
}

Perhaps the following equivalent code can be added to IsAny.

// Recursively try multi-error causes, if applicable. 
 		for _, me := range errbase.UnwrapMulti(c) { 
 			if Is(me, reference) { 
 				return true 
 			} 
 		} 

This fix was recently merged: #140 and the latest release contains the fix.