errors.IsAny does not support multi-error
intercept6 opened this issue · comments
Ryo Kato commented
It appears that errors.IsAny does not support multi-error.
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.
Lines 44 to 92 in c1cc191
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
}
}
David Hartunian commented
This fix was recently merged: #140 and the latest release contains the fix.