Learn what
-
What is wrong?
-
How do I create an error?
-
How to handle errors?
-
errors
Use of packages? -
What is an exception?
-
How to handle exceptions?
-
defer
The role of keywords? -
recover
Use of functions?
What is wrong
When writing code, not all cases can be handled. For the logic that cannot be handled, you need to use the error mechanism to tell the upper caller.
In go language, errors are treated as an interface type, which is not used like other languagestry/catch
To capture, simply pass a wrong type variable between functions or methods.
Create error
The creation error mentioned here is actually to implement the error interface. The interface is as follows:
type error interface {
Error() string
}
This interface is built in the go standard package. All error types created need to implement this interface. How to implement the interface? If you don’t understand, see the previous articleInterface。
1. errors.New
The go language has a built-in standard package for handling errors. You don’t need to implement it yourselferror
Interface, which has functions to help you handle, as follows:
import "errors"
var ErrNotFound = errors.New("not found")
Importerrors
Package, callingNew
Function creates an error and saves it toErrNotFound
Variable, the error message isnot found
。
2. fmt.Errorf
fmt
There is also a function to create errors in the standard packageErrorf
, this function can use placeholders to set error messages, such aserrors.New
Functions are more flexible.
import "fmt"
var ErrHuman = fmt. Errorf ("% s does not meet our human requirements", "Lao Miao")
3. Custom error type
If the above two methods are not flexible enough, you can customize the error type.
type ErrorPathNotExist struct {
Filename string
}
func (*ErrorPathNotExist) Error() string {
Return "the file path does not exist."
}
var ErrNotExist error = &ErrorPathNotExist{
Filename: "./main.go",
}
-
Customized a
ErrorPathNotExist
Structure, which realizeserror
Interface. -
Created a
ErrNotExist
Wrong type variable.
If you don’t understand how to apply this, don’t worry, look down.
Supplementary knowledge points: if the receiver of the method is not used, it can be directly omitted, for example:
func (*ErrorPathNotExist)
, if not omitted, this is it:func (e *ErrorPathNotExist)
Of course, you can also use the underscore “” Replacing “e” is just not necessary.
Print error
In project development, errors are often carried through the return value of functions or methods, and the return position is usually placed in the last bit.
// error/file.go
package main
import "io/ioutil"
//Read file contents
func LoadConfig() (string, error) {
filename := "./config.json"
b, err := ioutil.ReadFile(filename)
if err != nil {
return "", err
}
content := string(b)
if len(content) == 0 {
return "", errors. New ("content is empty")
}
return content, nil
}
-
ReadFile
Function to read the contents of the “config. JSON” file. -
(string, error)
Returns two values, the first is the file content and the second is an error. -
err != nil
Used to judge whether there is an error, if anyreturn
Direct return. -
string(b)
The type of variable B is[]byte
, the operation is to[]byte
Type tostring
。 -
An error judgment of “empty content” is added, which can also be directly saved to variables and returned.
var ErrEmpty = errors. New ("content is empty")
func LoadConfig() (string, error) {
// ...
return "", ErrEmpty
// ...
}
Now, assuming that the “config. JSON” file does not exist, callLoadConfig
Function to see the result.
package main
import (
"fmt"
"log"
)
func main() {
content, err := LoadConfig()
if err != nil {
log.Fatal(err)
}
fmt. Println ("content:", content)
}
//Output
2021/09/23 16:57:25 open ./config.json: The system cannot find the file specified.
-
When
err
Not equal tonil
Print error and exit the program. -
log
The standard package contains a set of functions for printing logs. -
log.Fatal
Function prints the log and terminates the program execution downward. -
The output error message shows that the specified file was not found.
-
When printing errors, you can also use
fmt
Package, for example:fmt.Println(err)
No, just output informationlog
Bao duo.
os.Exit
This function notifies the program to exit, and the logic after this function will not be executed. When calling, you need to specify the exit code. When it is 0, it indicates that you exit the program normally.
os.Exit(0)
Do not actively call this function, that is, when the program ends naturally from the main function, the default exit code is 0. When using the writing tool, you may see the successful exit code information. For example, I use Golan. After executing the code, the following information will be displayed at the end of the output result.
Process finished with exit code 0
If you exit abnormally, the exit code is non-0, and 1 is usually used to indicate an unknown error.
os.Exit(1)
in uselog.Fatal
Function is called internallyos.Exit(1)
。
Error processing
1. Wrong splicing
When an error is returned, if you want to carry an additional error message, you can usefmt.Errorf
, modify nowLoadConfig
Function.
func LoadConfig() (string, error) {
filename := "./config.json"
b, err := ioutil.ReadFile(filename)
if err != nil {
return "", fmt. Errorf ("error reading file:% v", ERR)
}
// ...
}
%v
The placeholder represents the value of the obtained data. In this section, it represents an error message. The use of the placeholder will be explained in detail later.
Now re execute the above proceduremain
Function, or assume that the “config. JSON” file does not exist.
// ...
content, err := LoadConfig()
if err != nil {
log.Fatal(err)
}
//...
//Output
2021 / 09 / 24 11:37:33 error reading file: open/ config. json: The system cannot find the file specified.
LoadConfig
Function returnserr
The variable carries an additional error message, but there is a problem. The additional error and the original error message are mixed together, so they cannot be separated.
2. Error nesting and errors Unwrap
The above-mentioned “mixed error messages lead to inseparability” problem. If you don’t understand it, you can take another look at this one and you should suddenly be enlightened.
Error nesting is similar to the figure above,err1
Nested classerr2
,err2
You can also continue nesting. If you want to start fromerr1
Get inerr2
Just peel one layer, like onions, and look inside layer by layer.
How to implement this nested relationship, or usefmt.Errorf
Function, just use another placeholder%w
,w
My full English name iswrap
。
Continue to modifyLoadConfig
Function, as follows:
func LoadConfig() (string, error) {
filename := "./config.json"
b, err := ioutil.ReadFile(filename)
if err != nil {
return "", fmt. Errorf ("error reading file:% w", ERR)
}
// ...
}
Do it nowmain
Function, that is, callLoadConfig
This function and prints the error.
2021 / 09 / 24 18:07:14 error reading file: open/ config. json: The system cannot find the file specified.
Is it found that the error result has not changed, then modify itmain
Function.
package main
import (
"errors"
"fmt"
"log"
)
func main() {
content, err := LoadConfig()
if err != nil {
log.Fatal(errors.Unwrap(err))
}
fmt. Println ("content:", content)
}
//Output
2021/09/24 18:11:09 open ./config.json: The system cannot find the file specified.
In case of printing error, an error is addederrors.Unwrap
Function, which is used to take out nested errors, and then look at the output results. The additional error message “error reading file:” it’s gone.
3. Custom error type
We talked about how to customize error types. Now we talk about how to apply your customized errors. Next, we willLoadConfig
The error with empty content in the function is changed to a user-defined error type.
func LoadConfig() (string, error) {
filename := "./config.json"
// ...
if len(content) == 0 {
return "", &FileEmptyError{
Filename: filename,
Err: errors. New ("content is empty"),
}
}
return content, nil
}
FileEmptyError
Is a custom error type, the same implementationerror
Interface.
type FileEmptyError struct {
Filename string
Err error
}
func (e *FileEmptyError) Error() string {
return fmt.Sprintf("%s %v", e.Filename, e.Err)
}
Call nowLoadConfig
Function. Now assume that the “config. JSON” file exists, but the content is empty. The results are as follows:
content, err := LoadConfig()
if err != nil {
if v, ok := err.(*FileEmptyError); ok {
fmt.Println("Filename:", v.Filename)
}
log.Fatal(err)
}
//Output
Filename: ./config.json
2021/09/27 14:36:40 ./ config. JSON content is empty
-
take
err
The interface type of the variable is inferred as*FileEmptyError
Type and outputFilename
Field. -
Print custom error content.
If you want to useerrors.Unwrap
Function, you need to implement itWrapper
Interface,fmt.Errorf
In function%w
The placeholder bottom layer implements this interface.
type Wrapper interface {
// Unwrap returns the next error in the error chain.
// If there is no next error, Unwrap returns nil.
Unwrap() error
}
We just need to achieve itUnwrap
Method is OK.
func (e *FileEmptyError) Unwrap() error {
return e.Err
}
Come down and experiment by yourself. I won’t be wordy here.
Wrong judgment
For a function or method, the error returned is often more than one error result. If you want to have different processing logic for different error results, you must judge the error results at this time.
// ...
var (
ErrNotFoundRequest = errors.New("404")
ErrBadRequest = errors. New ("request exception")
)
func GetError() error {
// ...
//Error 1
return ErrNotFoundRequest
// ...
//Error 2
return ErrBadRequest
// ...
//Error 3
path := "https://printlove.com"
return fmt.Errorf("%s:%w", path, ErrNotFoundRequest)
// ...
}
GetError
The function does not write specific logic, but only shows the return of three errors. Let’s see how to judge these situations.
1. Simplest “= =”
The simplest way is to use “= =” to judge the wrong result.
err := GetError()
if err == ErrNotFoundRequest {
//Error 1
} else if err == ErrBadRequest {
//Error 2
}
There is a problem with this way of judgment, “error 3” does not meet these two requirementsif
Judging, but in terms of misclassification, it belongs toErrNotFoundRequest
Error, only the requested address data is splicedpath
, look down at another way to judge.
2. errors.Is
useerrors.Is
Function to solve the judgment problem of “error 3”. Why?
Placeholder used in error 3%w
, it willErrNotFoundRequest
Error embedded in it,**Is
The function is used to split and judge the errors layer by layer * * until they are successful or there are no nested errors. If you don’t understand, you can combine the above figure.
The function is defined as follows:
func Is(err, target error) bool
-
err
The parameter is the error to be judged. -
target
The parameter is the wrong object to compare.
Use the following:
err := GetError()
if errors.Is(err, ErrNotFoundRequest) {
//Error 1, error 3
} else if errors.Is(err, ErrBadRequest) {
//Error 2
}
-
When we call a function or a method in a specific project, we do not know whether the underlying layer is nested correctly. If it is not clear, we will use it uniformly.
Is
Function. -
Error 1
andError 2
It can be judged without nesting.
3. errors.As
This anderrors.Is
Functions are similar, except that the function only judges the error type, anderrors.Is
The function judges not only the type, but also the value (error message).
// error/as.go
package main
import (
"errors"
"fmt"
)
type ErrorString struct {
s string
}
func (e *ErrorString) Error() string {
return e.s
}
func main() {
var targetErr *ErrorString
err := fmt.Errorf("new error:[%w]", &ErrorString{s: "target err"})
fmt.Println(errors.As(err, &targetErr))
}
//Output
true
targetErr
Variables have several requirements:
-
No initialization is required.
-
Must be a pointer type and implement
error
Interface. -
As
Function not acceptednil
Therefore, it cannot be used directlytargetErr
Variable to use its reference&targetErr
。
What is an exception
The error is the above, and its occurrence will not cause the program to exit abnormally.
So what happens when you quit abnormally? For example:
-
Subscript out of bounds
-
Divisor is 0
-
wait
Usually, the exception is thrown when you don’t notice the code problem. Of course, you can throw it on your own initiative.
panic
usepanic
The function can actively throw exceptions. The format of the function is as follows:
func panic(v interface{})
v
If the parameter is an empty interface type, it means that any type of data can be accepted.
Example:
Panic ("I'm an exception")
//Output
Panic: I'm an exception
goroutine 1 [running]:
main.main()
C:/workspace/go/src/gobasic/panic/panic.go:4 +0x45
exit status 2
Several points can be seen from the output results:
-
Print out the specific exception location, which is called stack information.
-
The program terminates with exit code 2.
Handling exceptions
Whether it is actively throwing exceptions or passively throwing exceptions when your program has bugs, these are very serious problems when we write the project, because it will cause our program to exit abnormally.
In other languages, bytry/catch
The mechanism can catch exceptions to ensure the normal operation of the program, which is used in go languagerecover
Function caught exception.
Before learning this function, you should understand another keyworddefer
。
1. defer
It’s not a function, it’s just a keyword. The statement followed by this keyword will be executed late. It will be executed in advance when the function or method ends normally or there is an abnormal interrupt.
package main
import "fmt"
func main() {
defer func() {
fmt.Println("defer")
}()
fmt.Println("main")
panic("panic")
}
//Output
main
defer
panic: panic
...
-
defer
Keyword is followed by an anonymous function call. Of course, functions with names can also be used. -
encounter
defer
Keyword, the subsequent statements will delay execution, so the “main” string is output first. -
panic
Throw an exception, so execute before exitingdefer
sentence. -
If called
os.Exit
Function,defer
The following statement will not be executed.
If an error occurs in a function or methodMultipledefer
Statement, that will adopt the first in, last out principle, that is, the first onedefer
Statements are executed after, and those that appear after are executed first.
Example:
package main
import "fmt"
func Fun1() {
fmt. Println ("guess when I'll output?")
}
func main() {
defer func() {
fmt.Println("defer")
}()
defer Fun1()
fmt.Println("main")
panic("panic")
}
//Output
main
Guess when I output?
defer
panic: panic
...
Nowdefer
The concept of keyword may not be known where it is used in practice. Let me give an example.
// defer/main.go
//Copy file
//Copy srcname path file to dstname path file
func CopyFile(srcName, dstName string) (written int64, err error) {
src, err := os.Open(srcName)
if err != nil {
return
}
defer src.Close()
dst, err := os.Create(dstName)
if err != nil {
return
}
defer dst.Close()
return io.Copy(dst, src)
}
There are two in the codedefer
Keyword. The following statements are used to close files and release resources.
If you do not use this keyword, close the fileClose
The function must be written inio.Copy
Function, because the function also uses file resources, close it in advance, and it’s over. So, usedefer
Keyword will delay execution, so there is no need to consider when the file is used.
Of course, this is not the only case. Just remember that any statement you want to process before the end of a function or method can be useddefer
keyword.
2. recover
I seedefer
Keyword, before an exception occurs in the programdefer
Statement is executed first, so indefer
The following statement can intercept exceptions in advance.
// panic/recover.go
package main
import "fmt"
func main() {
defer func() {
if err := recover(); err != nil {
fmt. Println ("I captured:", ERR)
fmt. Println ("I'm fine")
}
}()
Panic ("I'm an exception")
}
//Output
I caught: I am abnormal
I'm fine
recover
Return exception information.
Whether you throw an exception actively or passively,recover
Functions can capture, so as to ensure the normal operation of the program.
Suppose functionA
Many functions are called, and many of these functions are called again. As long as the following called functions are abnormal, the functionA
Mediumrecover
Functions will be able to capture. But there is one exception,Goroutine
The exception in cannot be caught and must be in the newGoroutine
Reuse inrecover
Function, which is an expected knowledge point. I’ll talk about it later.
summary
This chapter explains two concepts, “error” and “exception”. These two concepts are very important and must be understood. Another point is that when dealing with errors, we sometimes find that the wrong results are useless to us, so we can ignore them at this time.
When I write, I also want to make it as clear as possible. Some places will be wordy.
This work adoptsCC agreement, reprint must indicate the author and the link to this article