There have been some changes in the errors package in Go 1.13
These changes are intended to better support Go's error handling proposals. A new method has also been added in Go 1.20, which can handle multiple errors instead of third-party libraries, and this article will introduce these changes.
Because the content in the original Go errors is very simple, it may cause everyone to despise this package and are not so concerned about new changes. Let's introduce these new methods one by one.
Unwrap
If an err is implementedUnwrap
Function, thenWill return this err
unwrap
The result of the method, otherwise return nil. No general standard error is implementedUnwrap
Methods, such as, but a small number of errors have been implemented
Unwrap
Methods, such as,
、
、
、
etc.
For example, the following code:
(()) // nil _, err := ("tcp", ":80") ((err))
The first line becauseNo
Unwrap
method, so output nil. The failed return err is*
, It implementsUnwrap
Method, return to the underlying*
, so the output of the second line islookup : no such host
。
The most commonly used ones we use +
%w
Wrapping an error, such as the following code:
e1 := ("e1: %w", ) e2 := ("e2: %w + %w", e1, ) e3 := ("e3: %w", e2) e4 := ("e4: %w", e3) ((e4)) // e3: e2: e1: EOF + io: read/write on closed pipe
This code is wrapped layer by layer, and the finale4
All errors are included, we can useUnpack the package layer by layer until the bottom layer error. You can wrap multiple errors at once, such as the above
e2
, it containse1
andTwo errors.
We often pass the lowest error layer by layer when calling multiple layers, and we can use it +
%w
Packing error. When processing errors at the highest level, then layer by layerUnwrap
Unlock the error and process it layer by layer.
Is
Is
The function checks whether the tree of the error contains the specified target error.
What is an errorTree? The number of an error includes itself, and throughUnwrap
The error that unravels the method layer by layer. errorUnwrap
The return value of the method may be a single error or multiple errors. When multiple errors are returned, the depth-first way will be used to traverse and check to find the target error.
How can I find the target error? One situation is that this err is the target error. There is nothing to say about this, and the second is that this err is implemented.Is(err)
Method: throw target err intoIs
The method returns true.
So from the perspective of functionIs
The function is actually calledHas
The function is more appropriate.
Here is an example:
e1 := ("e1: %w", ) e2 := ("e2: %w + %w", e1, ) e3 := ("e3: %w", e2) e4 := ("e4: %w", e3) ((e4, )) // true ((e4, )) // true ((e4, )) // false
As
Is
is the number of traversal errors to check whether the target error is included.As
It is to traverse the number of errors, check each error, and see if it can be assigned from error to the target variable. If so, it will return true, and the target variable has been assigned, otherwise it will return false.
We can see the following exampleAs
Usage:
if _, err := ("non-existing"); err != nil { var pathError * if (err, &pathError) { ("failed at path:", ) } else { (err) } }
If the returned error tree contains*
,SoWill assign this error to
pathError
variable, and returns true, otherwise returns false. Our example just creates an error whose file does not exist, so it will output:failed at path: non-existing
A common mistake is that we use oneerror
Variable asAs
The second parameter of . The following example tmp is the error interface type, so origin can be assigned directly to tmp, soReturns true, and the value of tmp is the value of origin.
var origin = ("error: %w", ) var tmp = if (origin, &tmp) { (tmp) // error: EOF }
As
It is always so awkward to use. You have to declare a variable every time and then pass this variable toAs
Functions, after Go supports generics,As
It should be simplified into the following method:
func As[T error](err error "T error") (T, bool)
However, Go will not modify this API that causes incompatible, so we can only keep itAs
Function, adding a new function is a feasible method, no matter what it is calledIsA
、AsOf
stillAsTarget
Or something else.
If you have mastered the generics of Go, you can implement one by yourselfAs
Functions, such as the following code:
func AsA[T error](err error "T error") (T, bool) { var isErr T if (err, &isErr) { return isErr, true } var zero T return zero, false }
Write a piece of test code and we can see its effect:
type MyError struct{} func (*MyError) Error() string { return "MyError" } func main() { var err error = ("error: %w", &MyError{}) m, ok := AsA[*MyError](err "*MyError") // MyError does not implement error (Error method has pointer receiver) (m, ok) }
Everyone discussed it in #51945[1] for a while, but it ended in vain again.
Join
In our project, we sometimes need to deal with multiple errors, such as the following code:
func (s *Server) Serve() error { var errs []error if err := (); err != nil { errs = append(errs, err) } if err := (); err != nil { errs = append(errs, err) } if err := (); err != nil { errs = append(errs, err) } if len(errs) > 0 { return ("server error: %v", errs) } return nil }
In this code, we need to deal with three errors. If one error is not nil, then we return errors. Of course, in order to handle multiple errors, there were many third-party libraries available for us to use, for example
/multierr
/hashicorp/go-multierror
/cockroachdb/errors
But now, you don't have to build wheels or use third-party libraries, because it's added in Go 1.20Function, it can combine multiple errors into one error, such as the following code:
var e1 = var e2 = var e3 = var e4 = _, e5 := ("tcp", ":80") e6 := ("/path/to/nonexistent/file") var e = (e1, e2) e = (e, e3) e = (e, e4) e = (e, e5) e = (e, e6) (()) // The output is as follows, each err is one line // // EOF // io: read/write on closed pipe // multiple Read calls return no data or error // short buffer // dial tcp: lookup : no such host // remove /path/to/nonexistent/file: no such file or directory ((e)) // nil ((e, e6)) //true ((e, e3)) // true ((e, e1)) // true
You can useIs
Determine whether an error is included or usedAs
Extract the target error.
References
[1]
#51945: /golang/go/issues/51945
The above is the detailed analysis of the new changes of the errors package in Go 1.13. For more information about the changes of the Go1.13 errors package, please follow my other related articles!