This is the 17th day of my participation in the August More text Challenge. For details, see: August More Text Challenge

Exception handling & Resource management

Resource management means that when we open a file, we need to close it. After connecting to the database, we need to release. These things need to come in pairs, these things come in pairs, don’t forget to write the closing statement at the end. However, if something goes wrong and the program pops up in the middle, how do you ensure that open connections are closed? This is why error handling and resource management are considered together

Defer to invoke

The Go language implements resource management through the defer call

  1. Make sure the call happens when the function ends
Println(1) Println(2)} func main() {tryDefer()Copy the code
  1. The defer list is in and out

Println(2) : Println(2) : Println(2) : Println(2) : Println(2) : Println(2

func tryDefer() { defer fmt.Println(1) defer fmt.Println(2) fmt.Println(3) return fmt.Println(4) } func main() { TryDefer ()} output: 3 2 1Copy the code

It can be found that even if there is an exit in the middle, 1 and 2 can be normally typed out

Here is an example in a real world scenario

func writeFile(filename string) { file, err := os.Create(filename) if err ! = nil {panic(err)} defer file.close () writer := bufio.newwriter (file) Defer writer.flush () // Flush the file from memory for I :=0; i < 10; i++ { fmt.Fprintln(writer, "number :" + strconv.Itoa(i)) } } func main() { writeFile("abcd.txt") }Copy the code
  1. The parameter is calculated in the defer statement
func tryDefer() { for i:=0; i< 100; i++ { defer fmt.Println(i) if i == 30 { panic("stop!!!" )}}} Print the result: 30 29...... 3 2 1 0Copy the code

Even though the program exits with I =30, it will not print out 30 30s because I was calculated in the defer statement

The defer statement is often used as a pair of actions, such as open and close, disconnect, lock and unlock. There is no limit on how many times defer can be used

Error handling

This is illustrated in the following code

func openFile(filename string) { file, err := os.OpenFile(filename, os.O_EXCL|os.O_CREATE, 0666) if err ! = nil {panic(err)} defer file.close () writer := bufio.newwriter (file) Defer writer.flush () // Flush the file from memory for I :=0; i < 10; i++ { fmt.Fprintln(writer, "number :" + strconv.Itoa(i)) } } func main() { openFile("abcd.txt") }Copy the code

This program throws an error and terminates (this is what panic does).

panic: open abcd.txt: file exists
Copy the code

But just hanging up is very unfriendly, so something needs to be done. We need to see what the err is and whether we can refine the error. Friends can use their IDEA, enter OpenFile to find the source file corresponding to the error. You can see that the error is an interface type, and then there is an internal error method that returns a string

type error interface {
    Error() string
}
Copy the code

So when you get an error up here, you can do that

Println("Error: ", err.error ()) Output: Error: open abcd. TXT: file existsCopy the code

If you go in and look at the comments for the os.openFile () method, you can see that

If there is an error, it will be of type *PathError.
Copy the code

In other words, if an error occurs, the actual return is a *PathError type, in which case every part of the error can be retrieved

if err ! = nil { if pathError, ok := err.(*PathError); ! Ok {panic(err)} else {fmt.Println(patheror. Op, patheror. Path, patheror. err)}} Output: open abcd.txt file existsCopy the code

We can also create our own error handling methods.

err := errors.New("This is a error")
Copy the code

panic

What happens after panic executes

  • Stops the current function execution
  • Go back up, and execute each layer’s defer
  • If no recover is encountered, the program exits

The Go type system catches many compile-time errors, but others, such as out-of-bounds access to arrays or references to null Pointers, need to be checked at runtime. When these errors are detected while the Go language is running, outages occur

When a typical outage occurs, normal program execution terminates, all the delay functions in the Goroutine are executed, and then the program exits unexpectedly, leaving a log message. Exception messages include the value of downtime, which often represents some kind of error message, and each Goroutine displays a stack trace message of a function call during downtime. This log message can often be used to diagnose the cause of the problem without having to run the program again

The built-in panic function can take any value as an argument, and it’s the best way to handle a situation that “can’t happen.

recover

  • Use only in the defer call (in the middle of the program, you can’t call it)
  • The value of panic can be called in the defer call
  • If no, panic again

If the built-in RECOVER function was called inside the defer function and the function containing the defer statement went down, Recover will terminate the current down state and return the value of the down state. The function does not continue from the previous outage, but returns normally. If recover is run in any other case, it has no effect and returns nil

Recovering from outages in the same package helps simplify handling complex and unknown errors, but the general rule is that you should not try to recover from outages in another package. The public API should report errors directly, and you shouldn’t restore an outage of code that you don’t maintain, such as caller-provided callbacks, because you don’t know if it’s safe to do so

package main import "fmt" func tryRecover() { defer func() { r := recover() if err, ok := r.(error); ok { fmt.Println("Error occurred", Err)} else {panic(r)}}() b := 0 a := 5 / b FMT.Println(a)} func main() {tryRecover()} Error occurred runtime error: integer divide by zeroCopy the code