Skip to content

Commit 49c9ffd

Browse files
Merge pull request #17 from upfluence/am/with-stack-2
.: Implement WithStack2
2 parents 7f95bb5 + 872aaf0 commit 49c9ffd

3 files changed

Lines changed: 76 additions & 3 deletions

File tree

README.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,24 @@ if err != nil {
200200
}
201201
```
202202

203+
**`WithStack2[T any](v T, err error) (T, error)`** (Go 1.18+)
204+
205+
Adds a stack frame at the current location, while passing through a return value. This is useful for adding stack traces to errors from functions that return multiple values (value, error) in a single line.
206+
207+
If err is nil, returns (v, nil) unchanged. If err is not nil, returns (v, err_with_stack).
208+
209+
```go
210+
// Instead of this:
211+
result, err := externalLib.DoSomething()
212+
if err != nil {
213+
return result, errors.WithStack(err)
214+
}
215+
return result, nil
216+
217+
// You can write this:
218+
return errors.WithStack2(externalLib.DoSomething())
219+
```
220+
203221
**`WithFrame(err error, depth int) error`**
204222

205223
Adds a stack frame at a specific depth in the call stack.

example_stacktrace_test.go

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,21 +9,54 @@ import (
99
func ExampleWithStack() {
1010
// Simulate an error from an external library
1111
externalErr := fmt.Errorf("external library error")
12-
12+
1313
// Add stack trace at current location
1414
err := errors.WithStack(externalErr)
15-
15+
1616
fmt.Println(err)
1717
// Output: external library error
1818
}
1919

20+
// Result represents a simple computation result
21+
type Result struct {
22+
Value int
23+
}
24+
25+
// mockCompute simulates an external library function
26+
func mockCompute(input int) (Result, error) {
27+
if input < 0 {
28+
return Result{}, fmt.Errorf("negative input not allowed")
29+
}
30+
return Result{Value: input * 2}, nil
31+
}
32+
33+
func ExampleWithStack2() {
34+
// Demonstrates using WithStack2 to add stack traces inline while returning values
35+
compute := func(input int) (Result, error) {
36+
// WithStack2 adds stack trace in a single line
37+
return errors.WithStack2(mockCompute(input))
38+
}
39+
40+
// Success case - error is nil
41+
result, err := compute(5)
42+
fmt.Printf("Result: %+v, Error: %v\n", result, err)
43+
44+
// Error case - error gets stack trace added
45+
result, err = compute(-1)
46+
fmt.Printf("Result: %+v, Error: %v\n", result, err)
47+
48+
// Output:
49+
// Result: {Value:10}, Error: <nil>
50+
// Result: {Value:0}, Error: negative input not allowed
51+
}
52+
2053
func ExampleWithFrame() {
2154
// This helper function wraps errors with the caller's location
2255
wrapError := func(err error) error {
2356
// Skip 1 frame to capture the caller's location instead of this function
2457
return errors.WithFrame(err, 1)
2558
}
26-
59+
2760
err := wrapError(fmt.Errorf("original error"))
2861
fmt.Println(err)
2962
// Output: original error

stacktrace.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,28 @@ func WithStack(err error) error {
77
return WithFrame(err, 1)
88
}
99

10+
// WithStack2 wraps an error with a stack frame captured at the call site, while
11+
// passing through a return value. This is useful for adding stack traces to errors
12+
// from functions that return multiple values (value, error).
13+
//
14+
// If err is nil, returns (v, nil) unchanged. If err is not nil, returns (v, err_with_stack)
15+
// where err_with_stack includes the stack frame.
16+
//
17+
// Example:
18+
//
19+
// return errors.WithStack2(externalLib.DoSomething())
20+
//
21+
// This is equivalent to but more concise than:
22+
//
23+
// result, err := externalLib.DoSomething()
24+
// if err != nil {
25+
// return result, errors.WithStack(err)
26+
// }
27+
// return result, nil
28+
func WithStack2[T any](v T, err error) (T, error) {
29+
return v, WithFrame(err, 1)
30+
}
31+
1032
// WithFrame wraps an error with a stack frame at the specified depth in the call stack.
1133
// The depth parameter indicates how many stack frames to skip (0 = current frame).
1234
func WithFrame(err error, d int) error {

0 commit comments

Comments
 (0)