Documentation ¶
Overview ¶
Package errors is a drop-in replacement and extension of the Go standard library's package errors.
Index ¶
- Variables
- func Annotate(err error, format string, args ...any) (annotated error)
- func As(err error, target any) (ok bool)
- func Is(err, target error) (ok bool)
- func Join(errs ...error) error
- func List(msg string, errs ...error) (err error)deprecated
- func New(msg string) (err error)deprecated
- func Unwrap(err error) (unwrapped error)
- func WithDeferred(returned, deferred error) (result error)
- type Aser
- type Deferred
- type Error
- type Iser
- type Pair
- type Wrapper
- type WrapperSlice
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ErrUnsupported = stderrors.ErrUnsupported
ErrUnsupported indicates that a requested operation cannot be performed, because it is unsupported. For example, a call to os.Link when using a file system that does not support hard links.
Functions ¶
func Annotate ¶
Annotate annotates the error with the message, unless the error is nil. The last verb in format must be a verb compatible with errors, for example "%w".
In Defers ¶
The primary use case for this function is to simplify code like this:
func (f *foo) doStuff(s string) (err error) { defer func() { if err != nil { err = fmt.Errorf("bad foo %q: %w", s, err) } }() // … }
Instead, write:
func (f *foo) doStuff(s string) (err error) { defer func() { err = errors.Annotate(err, "bad foo %q: %w", s) }() // … }
At The End Of Functions ¶
Another possible use case is to simplify final checks like this:
func (f *foo) doStuff(s string) (err error) { // … if err != nil { return fmt.Errorf("doing stuff with %s: %w", s, err) } return nil }
Instead, you could write:
func (f *foo) doStuff(s string) (err error) { // … return errors.Annotate(err, "doing stuff with %s: %w", s) }
Warning ¶
This function requires that there be only ONE error named "err" in the function and that it is always the one that is returned. Example (Bad) provides an example of the incorrect usage of WithDeferred.
Example ¶
package main import ( "fmt" "github.com/AdguardTeam/golibs/errors" ) func main() { f := func(fn string) (err error) { defer func() { err = errors.Annotate(err, "reading %q: %w", fn) }() return errors.Error("not found") } err := f("non-existing") fmt.Println("with err :", err) f = func(fn string) (err error) { defer func() { err = errors.Annotate(err, "reading %q: %w", fn) }() return nil } err = f("non-existing") fmt.Println("without err :", err) }
Output: with err : reading "non-existing": not found without err : <nil>
Example (Bad) ¶
package main import ( "fmt" "github.com/AdguardTeam/golibs/errors" ) func main() { const errNotFound errors.Error = "not found" cond := true g := func() (err error) { return nil } f := func() error { if cond { err := g() if err != nil { return err } // BAD! This err is not the same err as the one that is // returned from the top level. defer func() { err = errors.Annotate(err, "f") }() } // This error is returned without an annotation. return errNotFound } // Outputs the error without an annotation. fmt.Println(f()) }
Output: not found
Example (End) ¶
package main import ( "fmt" "github.com/AdguardTeam/golibs/errors" ) func main() { f := func(fn string) (err error) { err = errors.Error("not found") // Some operations. return errors.Annotate(err, "reading %q: %w", fn) } err := f("non-existing") fmt.Println("with err :", err) f = func(fn string) (err error) { // Some operations. return errors.Annotate(err, "reading %q: %w", fn) } err = f("non-existing") fmt.Println("without err :", err) }
Output: with err : reading "non-existing": not found without err : <nil>
func As ¶
As finds the first error in err's chain that matches target, and if so, sets target to that error value and returns true. Otherwise, it returns false.
It calls errors.As from the Go standard library.
Example ¶
package main import ( "fmt" "os" "github.com/AdguardTeam/golibs/errors" ) func main() { if _, err := os.Open("non-existing"); err != nil { var pathError *os.PathError if errors.As(err, &pathError) { fmt.Println("Failed at path:", pathError.Path) } else { fmt.Println(err) } } }
Output: Failed at path: non-existing
func Is ¶
Is reports whether any error in err's chain matches target.
It calls errors.Is from the Go standard library.
Example ¶
package main import ( "fmt" "os" "github.com/AdguardTeam/golibs/errors" ) func main() { if _, err := os.Open("non-existing"); err != nil { if errors.Is(err, os.ErrNotExist) { fmt.Println("file does not exist") } else { fmt.Println(err) } } }
Output: file does not exist
func Join ¶ added in v0.12.1
Join returns an error that wraps the given errors. Any nil error values are discarded. Join returns nil if errs contains no non-nil values. The error formats as the concatenation of the strings obtained by calling the Error method of each element of errs, with a newline between each string.
It calls errors.Join from the Go standard library.
func List
deprecated
List wraps several errors into a single error with an additional message.
Deprecated: Use errors.Join instead.
Example ¶
package main import ( "fmt" "github.com/AdguardTeam/golibs/errors" ) func main() { const ( err1 errors.Error = "stage 1" err2 errors.Error = "stage 2" ) err := errors.List("fail") fmt.Printf("msg only : %q %v\n", err, errors.Unwrap(err)) err = errors.List("fail", err1) fmt.Printf("msg and err : %q %q\n", err, errors.Unwrap(err)) err = errors.List("fail", err1, err2) fmt.Printf("msg and errs : %q %q\n", err, errors.Unwrap(err)) }
Output: msg only : "fail" <nil> msg and err : "fail: stage 1" "stage 1" msg and errs : "fail: 2 errors: \"stage 1\", \"stage 2\"" "stage 1"
func New
deprecated
New returns an error that formats as the given msg. Each call to New returns a distinct error value even if the text is identical.
It calls errors.New from the Go standard library.
Deprecated: Use type Error and constant errors instead.
Example ¶
package main import ( "fmt" "github.com/AdguardTeam/golibs/errors" ) func main() { err := errors.New("emit macho dwarf: elf header corrupted") if err != nil { fmt.Print(err) } }
Output: emit macho dwarf: elf header corrupted
func Unwrap ¶
Unwrap returns the result of calling the Unwrap method on err, if err's type contains an Unwrap method returning error. Otherwise, Unwrap returns nil.
It calls errors.Unwrap from the Go standard library.
func WithDeferred ¶
WithDeferred is a helper function for deferred errors. For example, to preserve errors from the Close method, replace this:
defer f.Close()
With this:
defer func() { err = errors.WithDeferred(err, f.Close()) }
If returned is nil and deferred is non-nil, the returned error implements the Deferred interface. If both returned and deferred are non-nil, result has the underlying type of *Pair.
Warning ¶
This function requires that there be only ONE error named "err" in the function and that it is always the one that is returned. Example (Bad) provides an example of the incorrect usage of WithDeferred.
Example ¶
package main import ( "fmt" "github.com/AdguardTeam/golibs/errors" ) func main() { const ( errClose errors.Error = "close fail" errNotFound errors.Error = "not found" ) close := func(fn string) (err error) { return nil } f := func(fn string) (err error) { defer func() { err = errors.WithDeferred(err, close(fn)) }() return nil } err := f("non-existing") fmt.Println("without errs :", err) close = func(fn string) (err error) { return nil } f = func(fn string) (err error) { defer func() { err = errors.WithDeferred(err, close(fn)) }() return errNotFound } err = f("non-existing") fmt.Println("with returned err :", err) close = func(fn string) (err error) { return errClose } f = func(fn string) (err error) { defer func() { err = errors.WithDeferred(err, close(fn)) }() return nil } err = f("non-existing") fmt.Println("with deferred err :", err) close = func(fn string) (err error) { return errClose } f = func(fn string) (err error) { defer func() { err = errors.WithDeferred(err, close(fn)) }() return errNotFound } err = f("non-existing") fmt.Println("with both errs :", err) }
Output: without errs : <nil> with returned err : not found with deferred err : deferred: close fail with both errs : returned: "not found", deferred: "close fail"
Example (Bad) ¶
package main import ( "fmt" "github.com/AdguardTeam/golibs/errors" ) func main() { const ( errClose errors.Error = "close fail" errNotFound errors.Error = "not found" ) cond := true close := func(fn string) (err error) { return errClose } g := func() (err error) { return nil } f := func(fn string) error { if cond { err := g() if err != nil { return err } // BAD! This err is not the same err as the one that is // returned from the top level. defer func() { err = errors.WithDeferred(err, close(fn)) }() } // This error is the one that is actually returned. return errNotFound } // Only outputs the returned error and ignores the deferred one. fmt.Println(f("non-existing")) }
Output: not found
Types ¶
type Aser ¶
Aser is a copy of the hidden aser interface from the Go standard library. It is added here for tests, linting, etc.
type Deferred ¶
Deferred is the interface for errors that were returned by cleanup functions, such as Close. This is useful in APIs which desire to handle such errors differently, for example to log them as warnings.
Method Deferred returns a bool to mirror the behavior of types like net.Error and allow implementations to decide if the error is a deferred one dynamically. Users of this API must check it's return value as well as the result errors.As.
if derr := errors.Deferred(nil); errors.As(err, &derr) && derr.Deferred() { // … }
See https://dave.cheney.net/2014/12/24/inspecting-errors.
Example ¶
package main import ( "fmt" "github.com/AdguardTeam/golibs/errors" ) func main() { const ( errClose errors.Error = "close fail" errNotFound errors.Error = "not found" ) // logErr logs the error according to its severity level. logErr := func(err error) { if defErr := errors.Deferred(nil); errors.As(err, &defErr) && defErr.Deferred() { // Log deferred errors as warnings. fmt.Printf("warning: %s\n", errors.Unwrap(defErr)) } else { fmt.Printf("ERROR: %s\n", err) } } // Case 1: the function fails, but the cleanup succeeds. close := func(fn string) (err error) { return nil } f := func(fn string) (err error) { defer func() { err = errors.WithDeferred(err, close(fn)) }() return errNotFound } err := f("non-existing") logErr(err) // Case 2: the function succeeds, but the cleanup fails. close = func(fn string) (err error) { return errClose } f = func(fn string) (err error) { defer func() { err = errors.WithDeferred(err, close(fn)) }() return nil } err = f("non-existing") logErr(err) }
Output: ERROR: not found warning: close fail
type Error ¶
type Error string
Error is the constant error type.
See https://dave.cheney.net/2016/04/07/constant-errors.
Example ¶
package main import ( "fmt" "github.com/AdguardTeam/golibs/errors" ) func main() { const errNotFound errors.Error = "not found" f := func(fn string) (err error) { return fmt.Errorf("opening %q: %w", fn, errNotFound) } err := f("non-existing") fmt.Println("err :", err) fmt.Println("unwrapped :", errors.Unwrap(err)) }
Output: err : opening "non-existing": not found unwrapped : not found
type Iser ¶
Iser is a copy of the hidden iser interface from the Go standard library. It is added here for tests, linting, etc.
type Pair ¶
Pair is a pair of errors. The Returned error is the main error that has been returned by a function. The Deferred error is the error returned by the cleanup function, such as Close.
In pairs returned from WithDeferred, the Deferred error always implements the Deferred interface.
Example ¶
package main import ( "fmt" "github.com/AdguardTeam/golibs/errors" ) func main() { close := func(fn string) (err error) { return errors.Error("close fail") } f := func(fn string) (err error) { defer func() { err = errors.WithDeferred(err, close(fn)) }() return errors.Error("not found") } err := f("non-existing") fmt.Println("err :", err) fmt.Println("unwrapped :", errors.Unwrap(err)) fmt.Println() errPair := &errors.Pair{} if !errors.As(err, &errPair) { panic("err is not an *error.Pair") } defErr := errPair.Deferred.(errors.Deferred) fmt.Println("deferred :", defErr) fmt.Println("deferred unwrapped :", errors.Unwrap(defErr)) fmt.Println("deferred check :", defErr.Deferred()) }
Output: err : returned: "not found", deferred: "close fail" unwrapped : not found deferred : deferred: close fail deferred unwrapped : close fail deferred check : true
type Wrapper ¶
type Wrapper interface {
Unwrap() error
}
Wrapper is a copy of the hidden wrapper interface from the Go standard library. It is added here for tests, linting, etc.
type WrapperSlice ¶ added in v0.12.1
type WrapperSlice interface {
Unwrap() []error
}
WrapperSlice is a copy of the hidden wrapper interface added to the Go standard library in Go 1.20. It is added here for tests, linting, etc.