Skip to content

Commit 287a237

Browse files
committed
Move "underlying errors" into property.
benchmark old ns/op new ns/op delta BenchmarkAllocProperty/props0 83.0 67.7 -18.43% BenchmarkAllocProperty/props1 172 148 -13.95% BenchmarkAllocProperty/props2 292 262 -10.27% BenchmarkAllocProperty/props3 443 375 -15.35% BenchmarkAllocProperty/props4 624 530 -15.06% BenchmarkAllocProperty/props5 828 713 -13.89% BenchmarkAllocProperty/props6 1062 969 -8.76% BenchmarkAllocProperty/props7 1375 1099 -20.07% BenchmarkAllocProperty/props8 1591 1297 -18.48% benchmark old allocs new allocs delta BenchmarkAllocProperty/props0 1 1 +0.00% BenchmarkAllocProperty/props1 3 3 +0.00% BenchmarkAllocProperty/props2 6 6 +0.00% BenchmarkAllocProperty/props3 10 9 -10.00% BenchmarkAllocProperty/props4 14 13 -7.14% BenchmarkAllocProperty/props5 19 18 -5.26% BenchmarkAllocProperty/props6 25 24 -4.00% BenchmarkAllocProperty/props7 32 28 -12.50% BenchmarkAllocProperty/props8 37 33 -10.81% benchmark old bytes new bytes delta BenchmarkAllocProperty/props0 96 64 -33.33% BenchmarkAllocProperty/props1 240 176 -26.67% BenchmarkAllocProperty/props2 432 336 -22.22% BenchmarkAllocProperty/props3 672 496 -26.19% BenchmarkAllocProperty/props4 912 704 -22.81% BenchmarkAllocProperty/props5 1200 960 -20.00% BenchmarkAllocProperty/props6 1536 1264 -17.71% BenchmarkAllocProperty/props7 1920 1472 -23.33% BenchmarkAllocProperty/props8 2208 1728 -21.74%
1 parent a4dc379 commit 287a237

File tree

4 files changed

+42
-25
lines changed

4 files changed

+42
-25
lines changed

builder.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ type ErrorBuilder struct {
1212
errorType *Type
1313
message string
1414
cause error
15-
underlying []error
1615
mode callStackBuildMode
1716
isTransparent bool
1817
}
@@ -93,14 +92,14 @@ func (eb ErrorBuilder) WithConditionallyFormattedMessage(message string, args ..
9392

9493
// Create returns an error with specified params.
9594
func (eb ErrorBuilder) Create() *Error {
96-
return &Error{
95+
err := &Error{
9796
errorType: eb.errorType,
9897
message: eb.message,
9998
cause: eb.cause,
100-
underlying: eb.underlying,
10199
transparent: eb.isTransparent,
102100
stackTrace: eb.assembleStackTrace(),
103101
}
102+
return err
104103
}
105104

106105
type callStackBuildMode int

error.go

Lines changed: 34 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,15 @@ import (
1010
// At the moment of creation, Error collects information based on context, creation modifiers and type it belongs to.
1111
// Error is mostly immutable, and distinct errors composition is achieved through wrap.
1212
type Error struct {
13-
message string
14-
errorType *Type
15-
cause error
16-
underlying []error
17-
stackTrace *stackTrace
18-
transparent bool
19-
properties *propertyMap
13+
message string
14+
errorType *Type
15+
cause error
16+
stackTrace *stackTrace
17+
// properties are used both for public properties inherited through "transparent" wrapping
18+
// and for some optional per-instance information like "underlying errors"
19+
properties *propertyMap
20+
transparent bool
21+
hasUnderlying bool
2022
}
2123

2224
var _ fmt.Formatter = (*Error)(nil)
@@ -35,21 +37,25 @@ func (e *Error) WithProperty(key Property, value interface{}) *Error {
3537
// Note that these errors make no other effect whatsoever: their traits, types, properties etc. are lost on the observer.
3638
// Consider using errorx.DecorateMany instead.
3739
func (e *Error) WithUnderlyingErrors(errs ...error) *Error {
38-
errorCopy := *e
39-
40-
newUnderying := make([]error, 0, len(e.underlying)+len(errs))
41-
newUnderying = append(newUnderying, e.underlying...)
40+
underlying := e.underlying()
41+
newUnderlying := underlying
4242

4343
for _, err := range errs {
4444
if err == nil {
4545
continue
4646
}
4747

48-
newUnderying = append(newUnderying, err)
48+
newUnderlying = append(newUnderlying, err)
4949
}
5050

51-
errorCopy.underlying = newUnderying
52-
return &errorCopy
51+
if len(newUnderlying) == len(underlying) {
52+
return e
53+
}
54+
55+
l := len(newUnderlying) // note: l > 0, because non-increased 0 length is handled above
56+
errorCopy := e.WithProperty(propertyUnderlying, newUnderlying[:l:l])
57+
errorCopy.hasUnderlying = true
58+
return errorCopy
5359
}
5460

5561
// Property extracts a dynamic property value from an error.
@@ -185,18 +191,29 @@ func (e *Error) messageWithUnderlyingInfo() string {
185191
}
186192

187193
func (e *Error) underlyingInfo() string {
188-
if len(e.underlying) == 0 {
194+
if !e.hasUnderlying {
189195
return ""
190196
}
191197

192-
infos := make([]string, 0, len(e.underlying))
193-
for _, err := range e.underlying {
198+
underlying := e.underlying()
199+
infos := make([]string, 0, len(underlying))
200+
for _, err := range underlying {
194201
infos = append(infos, err.Error())
195202
}
196203

197204
return fmt.Sprintf("(hidden: %s)", joinStringsIfNonEmpty(", ", infos...))
198205
}
199206

207+
func (e *Error) underlying() []error {
208+
if !e.hasUnderlying {
209+
return nil
210+
}
211+
// Note: properties are used as storage for optional "underlying errors".
212+
// Chain of cause should not be traversed here.
213+
u, _ := e.properties.get(propertyUnderlying)
214+
return u.([]error)
215+
}
216+
200217
func (e *Error) messageText() string {
201218
if e.Cause() == nil {
202219
return e.message

error_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -101,9 +101,9 @@ func TestImmutableError(t *testing.T) {
101101
require.True(t, err.errorType.IsOfType(err2.errorType))
102102
require.Equal(t, err.message, err2.message)
103103

104-
require.Len(t, err.underlying, 0)
105-
require.Len(t, err1.underlying, 1)
106-
require.Len(t, err2.underlying, 2)
104+
require.Len(t, err.underlying(), 0)
105+
require.Len(t, err1.underlying(), 1)
106+
require.Len(t, err2.underlying(), 2)
107107
})
108108
}
109109

property.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,9 @@ func ExtractProperty(err error, key Property) (interface{}, bool) {
6969
}
7070

7171
var (
72-
propertyContext = RegisterProperty("ctx")
73-
propertyPayload = RegisterProperty("payload")
72+
propertyContext = RegisterProperty("ctx")
73+
propertyPayload = RegisterProperty("payload")
74+
propertyUnderlying = RegisterProperty("underlying")
7475
)
7576

7677
func newProperty(label string) Property {

0 commit comments

Comments
 (0)