Skip to content

errors.IsAny does not support multi-error #135

@intercept6

Description

@intercept6

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 
 			} 
 		} 

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions