Documentation
¶
Overview ¶
Package try emulates aspects of the ill-fated "try" proposal using generics. See https://golang.org/issue/32437 for inspiration.
The model is two verbs:
- The E family of functions raise an error: they strip a final error return, panicking with a TryError if it is non-nil.
- A deferred handler catches it: Handle stores it into a returned error, Recover passes it to a func(error), and Forward passes it to a func(...any) sink such as log.Fatal or testing.TB.Fatal.
Example usage:
func Fizz(...) (..., err error) {
defer try.Handle(&err, func(err error) error {
if err == io.EOF {
return io.ErrUnexpectedEOF
}
return err
})
... := try.E2(Buzz(...))
return ..., nil
}
This package is a sharp tool and should be used with care. Quick and easy error handling can occlude critical error handling logic. Panic handling generally should not cross package boundaries or be an explicit part of an API.
Package try is a good fit for short Go programs and unit tests where development speed is a greater priority than reliability. Since the E functions panic if an error is encountered, recovering in such programs is optional.
Code before try:
func (a *MixedArray) UnmarshalNext(uo json.UnmarshalOptions, d *json.Decoder) error {
switch t, err := d.ReadToken(); {
case err != nil:
return err
case t.Kind() != '[':
return fmt.Errorf("got %v, expecting array start", t.Kind())
}
if err := uo.UnmarshalNext(d, &a.Scalar); err != nil {
return err
}
if err := uo.UnmarshalNext(d, &a.Slice); err != nil {
return err
}
if err := uo.UnmarshalNext(d, &a.Map); err != nil {
return err
}
switch t, err := d.ReadToken(); {
case err != nil:
return err
case t.Kind() != ']':
return fmt.Errorf("got %v, expecting array end", t.Kind())
}
return nil
}
Code after try:
func (a *MixedArray) UnmarshalNext(uo json.UnmarshalOptions, d *json.Decoder) (err error) {
defer try.Handle(&err)
if t := try.E1(d.ReadToken()); t.Kind() != '[' {
return fmt.Errorf("found %v, expecting array start", t.Kind())
}
try.E(uo.UnmarshalNext(d, &a.Scalar))
try.E(uo.UnmarshalNext(d, &a.Slice))
try.E(uo.UnmarshalNext(d, &a.Map))
if t := try.E1(d.ReadToken()); t.Kind() != ']' {
return fmt.Errorf("found %v, expecting array end", t.Kind())
}
return nil
}
Quick tour of the API ¶
The E family of functions all remove a final error return, panicking with a TryError if it is non-nil. The TryError captures a stack trace at the point it was raised. It is an ordinary error: errors.Is and errors.As see through it, fmt's "%+v" verb prints the stack trace, and it marshals to JSON with the trace included.
Handle recovers from that panic and allows assignment of the error to a return error value, optionally wrapping it. Other panics are not recovered.
func f() (err error) {
defer try.Handle(&err)
...
}
Handle accepts wrap functions that are applied in order, e.g. to add context:
func foo(i int) (err error) {
defer try.Handle(&err, func(err error) error {
return fmt.Errorf("unable to foo %d: %w", i, err)
})
...
}
Forward passes the error to fn, wrapped with file and line information. It inter-operates well with testing.TB and log.Fatal.
func TestFoo(t *testing.T) {
defer try.Forward(t.Fatal)
...
}
func main() {
defer try.Forward(log.Fatal)
...
}
Recover is like Forward, but it passes the error to a func(error). The error carries the stack trace, accessible via errors.As or "%+v".
func f() {
defer try.Recover(func(err error) {
var te *try.TryError
if errors.As(err, &te) {
// do something useful with te.StackTrace()
}
})
...
}
Index ¶
- func E(err error)
- func E1[A any](a A, err error) A
- func E2[A, B any](a A, b B, err error) (A, B)
- func E3[A, B, C any](a A, b B, c C, err error) (A, B, C)
- func E4[A, B, C, D any](a A, b B, c C, d D, err error) (A, B, C, D)
- func Forward(fn func(...any))
- func Handle(errptr *error, wrap ...func(error) error)
- func Recover(fn func(err error))
- type TryError
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Forward ¶
func Forward(fn func(...any))
Forward recovers an error previously raised with an E function and passes it to fn. The error formats with the file and line of the frame in which it occurred. Forward pairs well with testing.TB.Fatal and log.Fatal, which are func(...any) and so cannot be passed to Recover.
func Handle ¶
Handle recovers an error previously raised with an E function and stores it into errptr. Any wrap functions are applied in order before storing, e.g. to add context. The stored error chain still contains the *TryError, so the captured stack survives for callers using errors.As or "%+v".
Types ¶
type TryError ¶
type TryError struct {
// contains filtered or unexported fields
}
TryError is the error raised by the E family of functions and recovered by this package's handlers. It wraps an underlying error and captures the call stack at the point an E function observed a non-nil error.
TryError is exported so that callers up the stack can recover the trace via errors.As(err, new(*TryError)), the fmt "%+v" verb, or json.Marshal. It is the marker type this package uses to distinguish its own panics from foreign ones.
func (*TryError) Format ¶
Format implements fmt.Formatter:
%s, %v "file:line: message" (innermost frame, == Error) %+v message followed by the full stack (pkg/errors convention) %q quoted %s
func (*TryError) MarshalJSON ¶
MarshalJSON marshals the error message together with the stack trace. Each frame is a single "function:file:line" string. Without this, an error marshals to "{}" since it has no exported fields.
func (*TryError) StackTrace ¶
StackTrace resolves and returns the captured frames, innermost first. Resolution is lazy: nothing is symbolized until this is called.
Directories
¶
| Path | Synopsis |
|---|---|
|
Command example demonstrates the try package: the E functions raise errors, a deferred handler catches them, and the resulting *try.TryError carries a stack trace that survives as an ordinary error.
|
Command example demonstrates the try package: the E functions raise errors, a deferred handler catches them, and the resulting *try.TryError carries a stack trace that survives as an ordinary error. |