Earlier today, a package named x errors was silently updated in golang/x/exp, which is very similar to the name of another package named errors under golang/x/exp, and even the introduction is the same:
Package errors implements functions to manipulate errors. This package implements the Go 2 draft designs for error inspection and printing
As far as the current situation is concerned, the form of handling errors has basically been finalized. The way of handling errors is similar to another github.com/pkg/errors package before, but the details are different.
How to deal with error s?
Before Introduction article The design idea of github.com/pkg/errors package has been mentioned in this paper, and this idea has been inherited in the implementation of Go team. Start with a small example:
package main import ( "fmt" "golang.org/x/exp/xerrors" ) func raiseError() error { return xerrors.New("a new error") } func main() { err := xerrors.Errorf("raiseError: %w", raiseError()) fmt.Println(err) }
Output results:
raiseError: a new error
It looks very similar to the previous github.com/pkg/errors display. xerrors.Errorf serves as the function of errors.Wrap. It is worth mentioning that% w, which is used for wrapping errors, will also be used in subsequent validation errors.
At the same time, this package also contains several very useful auxiliary functions, namely: verifying the error type method Is, error type conversion method As, error relation chain disassembly method Opaque and extracting the inner error method Unwrap. We can illustrate this relationship with a simple demonstration:
var ( ErrBase = xerrors.New("a new error") ) func main() { err := xerrors.Errorf("raiseError: %w", ErrBase) fmt.Println(ErrBase == ErrBase) // The same address fmt.Println(err == ErrBase) // Different after packaging based on ErrBase fmt.Println(xerrors.Is(err, ErrBase)) // Verify that it is ErrBase-based fmt.Println(xerrors.Opaque(err) == err) // Not the same address after the chain is broken fmt.Println(xerrors.Is(xerrors.Opaque(err), ErrBase)) // Unable to determine a relationship after dissolving the chain of relationships fmt.Println(xerrors.Unwrap(err) == ErrBase) // Getting the underlying error is the original error with the same address }
The output results are as follows:
true false true false false true
Even after wrapping multiple layers, the error types can be correctly identified by the Is method:
func main() { err := xerrors.Errorf("raiseError: %w", ErrBase) err2 := xerrors.Errorf("wrap#01: %w", err) err3 := xerrors.Errorf("wrap#02: %w", err2) fmt.Println(xerrors.Is(err, ErrBase)) fmt.Println(xerrors.Is(err3, ErrBase)) // Identify relationships correctly and print them as true }
If you need to print detailed call links, you can output them through standard library fmt-related functions, such as Printf or Sprintf.
func main() { err := xerrors.Errorf("raiseError: %w", ErrBase) err2 := xerrors.Errorf("wrap#01: %w", err) err3 := xerrors.Errorf("wrap#02: %w", err2) fmt.Printf("%+v\n", err3) }
The output link is as follows:
wrap#02: main.main /.../error.go:16 - wrap#01: main.main /.../error.go:15 - raiseError: main.main /.../error.go:14 - a new error: main.init /.../error.go:10
Note, however, that when packing with% w, a single error can only be wrapped once. For example, when two errors are wrapped at the same time, they will be unrecognizable:
var ( ErrBase = xerrors.New("a new error") ErrBase2 = xerrors.New("another new error") ) func main() { err := xerrors.Errorf("raiseError: %w, %w", ErrBase2, ErrBase2) fmt.Println(xerrors.Is(err, ErrBase)) fmt.Println(xerrors.Is(err, ErrBase2)) }
Land use
In the implementation of a specific project, if the library file provided by other languages is used, I suggest that a special basic error type ErrBase can be defined by referring to the implementation of other languages. All other error types are derived from this type. In the subsequent external use process, it will be faster to determine the error type only through xerros.Is. Quickly determine whether the error is from this package, and can use this function to handle exceptions from this third-party package independently.
If you have other suggestions and questions, you are also welcome to leave a message in the message area for discussion. In addition, according to the actual implementation code, there are some edge cases that are not considered or processed. Because it is an experimental library, the library has to bear the risks in the use process.