err3

package module
v0.0.0-...-ad59939 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 17, 2022 License: MIT Imports: 1 Imported by: 0

README

err2

The package provides tools for compact and composeable error handling. Instead of the traditional:

x, err := f()
if err != nil {
	return err
}

You can write:

x := try.Check1(f())

Fork

This is a fork of github.com/lainio/err2 with a different user facing API. Internally the panic/recovery mechanism of propagating errors is the same. However, there is no automatic mechanism for printing panics.

Structure

err2 has the following package structure:

  • The err2 (main) package includes declarative error handling functions.
  • The try package offers error checking functions.

They packages are de-coupled, but normally you would use both.

Error handling

Every function which uses err2 for error-checking should have at least one err2.Handle* function declared with defer. If this is ommitted, an error will panic up the stack until it finds such a function that will recover.

This is the simplest form of err2.Handle*.

func do() error {
	defer err2.Handlef(&err, "do")
	...
}

There is also

  • Handlew: wrap the error with %w instead of %v
  • Handle: call a function with the error

There are also helpers that are useful for catching errors and panics mostly in top-level functions: Catch, CatchAll, CatchTrace. Generally a program can use CatchAll at the top-level.

Error checks

The try package provides convenient helpers to check the errors. Since the Go 1.18 we have been using generics to have fast and convenient error checking.

For example, instead of

b, err := ioutil.ReadAll(r)
if err != nil {
        return err
}
...

we can call

b := try.Try1(ioutil.ReadAll(r))
...

but not without an error handler (Return, Annotate, Handle) or it just panics your app if you don't have a recovery call in the current call stack. However, you can put your error handlers where ever you want in your call stack. That can be handy in the internal packages and certain types of algorithms.

We think that panicking for the errors at the start of the development is far better than not checking errors at all.

Background

The original err2 implements similar error handling mechanism as drafted in the original check/handle proposal. This forked version encourages the single use of a defer statement at the top of the function and then using the try.TryX functions to explicitly declare the error handlers for a function, similar to this new proposal.

The package accomplishes this internally by using panic/recovery, which is less than ideal. However, it works out well because:

  • benchmarks show that when there is no error, there is no overhead
  • it helps annotate panics
  • it helps capture stack traces

In general code should not pass errors in performance sensitive paths. Normally if it does (for example APIs that use EOF as an error), the API is not well designed.

The mandatory use of the defer might prevent some code optimisations like function inlining. If you have highly performance sensitive code it is probably best not to use this library for now, particularly in functions that are not benchmarked.

Automatic And Optimized Stack Tracing

TODO

Documentation

Overview

Package err3 provides three main functionality:

  1. err3 package includes helper functions for error recovery and handling
  2. try package is for error checking and handling

The traditional error handling idiom in Go is roughly akin to

if err != nil { return err }

The err3 package drives programmers to focus more on error handling rather than checking errors. We think that checks should be so easy that we never forget them. The CopyFile example shows how it works:

// CopyFile copies source file to the given destination. If any error occurs it
// returns error value describing the reason.
func CopyFile(src, dst string) (err error) {
     // Add first error handler just to annotate the error properly.
     defer err3.Handlef(&err, "copy %s %s", src, dst)

     // Try to open the file. If error occurs now, err will be annotated and
     // returned properly thanks to above err3.Returnf.
     r := try.Try1(os.Open(src))
     defer r.Close()

     // Try to create a file. If error occurs now, err will be annotated and
     // returned properly.
     w := try.Try1(os.Create(dst))
     // Add error handler to clean up the destination file. Place it here that
     // the next deferred close is called before our Remove call.
     defer err3.Cleanup(&err, func() {
     	os.Remove(dst)
     })
     defer w.Close()

     // Try to copy the file. If error occurs now, all previous error handlers
     // will be called in the reversed order. And final return error is
     // properly annotated in all the cases.
     try.Try1(io.Copy(w, r))

     // All OK, just return nil.
     return nil
}

Error checks

The try package provides convenient helpers to check the errors. For example, instead of

b, err := ioutil.ReadAll(r)
if err != nil {
   return err
}

we can write

b := try.Try1(ioutil.ReadAll(r))

Note that try.ToX functions are as fast as if err != nil statements. Please see the try package documentation for more information about the error checks.

Stack Tracing

TODO

Error handling

The beginning of every function should contain an `err3.Handle*` to ensure that errors are caught. Otherwise errors will escape the function as a panic and you will be relying on calling functions to properly recover from panics.

Package try is a package for reducing error handling verbosity

Instead of 'x, err := f(); if err != nil { return handler(err) }' One writes: 'x := Try1(f(), handler)

If the error is not nil it is automatically thrown via panic. It is then caught by 'Handle'

  import (
	"github.com/gregwebs/err2"
	_ "github.com/gregwebs/try"
  )

  func do() (err error) {
    defer err2.Handlew(&err, "do")

    x := Try1(f())(Formatw("called f"))
  }

Package try is a package for try.TryX functions that implement the error checking. try.TryX functions check 'if err != nil' and if it throws the err to the error handlers, which are implemented by the err2 package.

All of the try package functions should be as fast as the simple 'if err != nil {' statement, thanks to the compiler inlining and optimization. Currently though there is an

Note that try.ToX function names end to a number (x) because:

"No variadic type parameters. There is no support for variadic type parameters,
which would permit writing a single generic function that takes different
numbers of both type parameters and regular parameters." - Go Generics

The leading number at the end of the To2 tells that To2 takes two different non-error arguments, and the third one must be an error value.

Currently only To, To1, To2, and To3 are implemented, but more could be added.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Check

func Check(err error)

Check is a helper function to immediately return error values without adding an if statement with a return. If an error occurs, it panics the error. You must use err2.Handle... at the top of your function to catch the error and return it instead of continuing the panic. the Try... functions an be used instead of Check... to add an error handler

func Check1

func Check1[T any](v T, err error) T

Check1 is the same as Check but passes along one extra value

func Check2

func Check2[T, U any](v1 T, v2 U, err error) (T, U)

Check2 is the same as Check but passes along two extra values

func Check3

func Check3[T, U, V any](v1 T, v2 U, v3 V, err error) (T, U, V)

Check2 is the same as Check but passes along three extra values

func Cleanup

func Cleanup(handler func()) func(error) error

func Fmt

func Fmt(format string, args ...any) func(error) error

func Fmtw

func Fmtw(format string, args ...any) func(error) error

func Try

func Try[E error](errE E) func(func(E) error, ...func(error) error)

Try is a helper function to immediately return error values without adding an if statement with a return. If the error value is non-nil, the handler function will be applied to it first. Then the non-nil error will be given to panic. You must use err2.Handle... at the top of your function to catch the error and return it instead of continuing the panic.

func Try1

func Try1[T any, E error](v T, err E) func(func(E) error, ...func(error) error) T

func Try2

func Try2[T, U any, E error](v1 T, v2 U, err E) func(func(E) error, ...func(error) error) (T, U)

func Try3

func Try3[T, U, V any, E error](v1 T, v2 U, v3 V, err E) func(func(E) error, ...func(error) error) (T, U, V)

Types

This section is empty.

Directories

Path Synopsis
Package assert includes runtime assertion helpers both for normal execution as well as a helper packager for Go's testing.
Package assert includes runtime assertion helpers both for normal execution as well as a helper packager for Go's testing.
Package try is a package for reducing error handling verbosity
Package try is a package for reducing error handling verbosity

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL