Description
Proposal Details
The current implementation of joinError
appends the given errors onto an internal []error
. However, it does not take into account itself, leading to unexpected internal structures. This is perhaps best illustrated by an example:
var merrA error
merrA = errors.Join(merrA, fmt.Errorf("one"))
merrA = errors.Join(merrA, fmt.Errorf("two"))
merrA = errors.Join(merrA, fmt.Errorf("three"))
var merrB error
merrB = errors.Join(merrB, fmt.Errorf("one"), fmt.Errorf("two"), fmt.Errorf("three"))
One would likely expect merrA
and merrB
to be structurally equal. While they "print out" the same error, their internal representation differs:
merrA = &joinError{
errs: []error{
fmt.Errorf("one"),
&joinError{
errs: []error{
fmt.Errorf("two"),
&joinError{
errs: []error{
fmt.Errorf("three"),
},
},
},
},
},
}
merrB = &joinError{
errs: []error{
fmt.Errorf("one"),
fmt.Errorf("two"),
fmt.Errorf("three"),
}
}
My proposal is that errors.Join
special-case the internal joinError
type to "unwrap" the inner errors and flatten the slice.
I believe this change is fully backwards-compatible with any documented APIs and public interfaces. There are no test cases that cover this expected nesting as shown in #65360.
I also believe this will lead to more performant error printing, since it reduces the need to recurse multierrors, but I do not have time to gather data and benchmark my gut feeling.
I could not find any corresponding issues, but "errors" and "unwrap" are not particularly unique keywords, so apologies if I missed them.