To construct the error

In the GO language, there is a predefined interface: error, which comes with an error () method that returns a string when called.

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

Calling this method returns the specific result of the current error. There are several ways to generate an error.

  • errors.New()
  • fmt.Errorf()

errors.New()

Calling errors.new () returns a structure of type error, which implements an error () method that returns what was passed in when the errors.new () method was called.

import (
	"errors"
	"fmt"
)

func divide(a, b int) (error, int) {
	if b == 0 {
    // If the dividend is 0, construct an error structure
		return errors.New("The dividend cannot be zero."), 0
	}
	var result = a / b
	return nil, result
}

func main(a) {
	var err error // The initial value of data of type error is nil, similar to null in JS
	var result int

	err, result = divide(1.0)

  if err == nil {
    // If err is nil, it is running properly
    fmt.Println("Calculated result", result)
  } else {
    // If err is not nil, there is an error
    // Call the error method of the error structure to print the cause of the error
    fmt.Println("Calculation error", err.Error())
  }
}
Copy the code

As you can see in the above code, the divider passed in is 0 because the divide method is called. Upon judgment, a structure of type error, constructed by errors.New, is thrown.

We print the result returned by calling the error.error () method to the console, and see that the result returned is the value of the New method passed in.

The result is as follows:

fmt.Errorf()

The error structure constructed by the fmt.errorf () method is similar to the result of calling the errors.new () method. The difference is that the FMT.Errorf() method does a formatting of the data.

func divide(a, b int) (error, int) {
	if b == 0 {
    // Format the argument once, and place the formatted string into error
		return fmt.Errorf("Data %d is invalid", b), 0
	}
	var result = a / b
	return nil, result
}

err, result := divide(1.0)
fmt.Println("Calculation error", err.Error())
Copy the code

The result is as follows:

Panic () and recover ()

panic()

Panic () is equivalent to actively stopping the program. When panic() is called, the cause of the interruption needs to be passed. After the call, the console outputs the reason for the interruption, along with the call stack at the time of the interruption. We can modify the previous code:

func divide(a, b int) (error, int) {
	if b == 0 {
    // If something goes wrong, stop the program
		panic("The dividend cannot be zero.")}var result = a / b
	return nil, result
}

func main(a) {
  err, result := divide(1.0)
  fmt.Println("Calculation error", err.Error())
}
Copy the code

At panic(), the program interrupts directly and prints out the cause of the interruption on the console.

Panic () can be understood as the operation of throw new Error() in the JS program. So, is there a way to terminate panic(), a try-catch operation, in Go and return the program to its normal running logic?

recover()

Before I cover the recover() method, I need to introduce another keyword from the GO language: defer.

The statements after defer are called before the function returns and are often used to release resources, catch errors, and log output.

func getData(table, sql) {
  deferDisconnection () DB := Establish connection (table) data := DB.select(sql)
  return data
}
Copy the code

The statements after defer are stored in a data structure similar to a stack, and at the end of the function, the statements defined are pushed off the stack in order, with those defined later being called first.

func divide(a, b int) int {
  defer fmt.Println("Divisor", b)
  defer fmt.Println(The dividend is equal to, a)

  result := a / b
  fmt.Println("Calculated as", result)
	return result
}

divide(10.2)
Copy the code

In the code above, when the function started to run, we first defined two output statements by defer, first the divisor and then the dividend.

The actual result is:

  • Output the calculation results first;
  • And then print the dividend;
  • Finally output divisor;

This and the previous statement defined by defer will be executed out of the stack at the end of the function, defined first and then executed. In addition to executing defer at the end of the function, the logic of defer will be followed first if there is an exception. That is to say, we will also run the statements in defer during the program interruption after calling panic().

Here we redefined the divide function by adding a defer statement before execution, followed by a self-executing function that calls the recover() method.

The recover() method is called and catches the exception thrown by the current panic() and returns nil if there is no exception.

func divide(a, b int) int {
  // Before interrupting, invoke the statements defined after defer
	defer func(a) {
		if err := recover(a); err ! =nil {
			fmt.Println("Catch error", err)
		}
	}()

	if b == 0 {
    // The function is interrupted
		panic("The dividend cannot be zero.")
		return 0
	}

	return a / b
}

result := divide(1.0)
fmt.Println("Calculated result", result)
Copy the code

After the above code runs, we find that the program that called panic() is restored, and the subsequent calculation results are printed normally.

This is a bit like try-catch logic, except that recover needs to be placed in the statement after the defer keyword, more like a combination of catch and finally.