This is the 17th day of my participation in the August Text Challenge.More challenges in August

What is wrong

An error indicates an exception in a program. Suppose we are trying to open a file that does not exist on the file system. This is an exception, and it represents an error.

Errors in Go are another type. Errors are represented by the built-in error type. Like other types, such as int, float64,. Error values can be stored in variables, returned from functions, and so on.

How to Use errors

Let’s start with an example program that tries to open a file that doesn’t exist.

Sample code:

package main

import (  
    "fmt"
    "os"
)

func main(a) {  
    f, err := os.Open("/test.txt")
    iferr ! =nil {
        fmt.Println(err)
        return
    }
  // Read or write files according to f
    fmt.Println(f.Name(), "opened successfully")}Copy the code

There are functions to open files in the OS package:

func Open(name string) (file *File, err error)

If the file has been successfully opened, the Open function returns file processing. If an error occurs while opening the file, a non-nil error is returned.

If a function or method returns an error, then by convention it must be the last value returned by the function. Therefore, the value returned by the Open function is the last value.

The conventional way to handle errors is to compare returned errors to nil. A nil value indicates that no error occurred, while a non-nil value indicates that an error occurred. In our example, we check to see if the error is nil. If it’s not nil, we just print an error and return it from the main function.

Running results:

open /test.txt: No such file or directory
Copy the code

We get an error indicating that the file does not exist.

Sample code:

package main

import (
    "fmt"
    "errors"
)

func checkAge(age int) error {
    if age < 0 {
        //return errors.New(" age cannot be negative ")
        // Where the Errorf function of the FMT package comes in. This function formats the error according to a format specifier and returns a string as a value to satisfy the error.
        return fmt.Errorf("Age is: %d, age cannot be negative", age)

    }
    fmt.Println("Age correct,", age)
    return nil

}

func test(a)(int.int, error)  {
    return 1.2, errors.New("La la la la")}func main(a) {
    /* There is no direct exception handling mechanism in GO as there is in the Java language. Instead, error in error.go is also a type. Errors of built-in interface types are normal interfaces that represent error conditions, and nil values mean there are no errors. * /
    var e1 error // <nil>
    fmt.Println(e1)
    fmt.Printf("%T\n", e1) // <nil>

    // Create an error object
    e2 := errors.New("Wrong")
    fmt.Println(e2)        / / error
    fmt.Printf("%T\n", e2) // *errors.errorString

    err:=checkAge(- 30)
    if err == nil{
        fmt.Println("ok。。。")}else{
        fmt.Println("Failure,", err)
    }

    a, b, err:= test()
    fmt.Println(a, b, err)

    p1:= new(int)
    fmt.Printf("%T,\n",p1)
    fmt.Println(*p1)

    p2:=new(string)
    fmt.Printf("%T\n",p2)
    fmt.Println(*p2)

}
Copy the code

Running results:

<nil> <nil> error *errors.errorString failed, age is: -30, age cannot be negative 1 2 la la la *int, 0 *stringCopy the code

Error type representation

The Go language provides a very simple error handling mechanism through a built-in error interface.

Let’s dig a little deeper and see how to define an error-type build. The error is an interface type with the following definition,

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

It contains a method with an Error () string. Any type that implements this interface can be used as an error. This method provides a description of the error.

When an Error is printed, the fmt.println function internally calls the Error() method to get a description of the Error. This is how the error description is printed on one line.

Don’t ignore mistakes

Never ignore a mistake. Rewrite an example that lists the names of all files that match the pattern, ignoring the error-handling code.

package main

import (  
    "fmt"
    "path/filepath"
)

func main(a) {  
    files, _ := filepath.Glob("[")
    fmt.Println("matched files", files)
}
Copy the code

We already know from the previous example that patterns are invalid. I ignored the error returned by the Glob function by using a blank identifier in the line number.

matched files []
Copy the code

Because we ignored the error, the output looks as if no file matches the pattern, but the pattern itself is malformed. So don’t ignore errors.

Custom error

The easiest way to create custom errors is to use the new feature of error packs.

Before creating a custom error with the new function, let’s see how it is implemented. An implementation of the new functionality in the error package is provided below.

// Package errors implements functions to manipulate errors.
  package errors

  // New returns an error that formats as the given text.
  func New(text string) error {
      return &errorString{text}
  }

  // errorString is a trivial implementation of error.
  type errorString struct {
      s string
  }

  func (e *errorString) Error(a) string {
      return e.s
  }
Copy the code

Now that we know how the new function works, let’s use it in our own program to create a custom error.

We will create a simple program that calculates the area of a circle and returns an error if the radius is negative.

We go through the Errorf function of the FMT package. This function returns a string as a value based on a format to satisfy the error.

Use the Errorf function:

package main

import (  
    "fmt"
    "math"
)

func circleArea(radius float64) (float64, error) {  
    if radius < 0 {
        return 0, fmt.Errorf(Area Calculation failed, RADIUS %0.2f is less than zero", radius)
    }
    return math.Pi * radius * radius, nil
}

func main(a) {  
    radius := 20.0
    area, err := circleArea(radius)
    iferr ! =nil {
        fmt.Println(err)
        return
    }
    fmt.Printf("The Area of circle % 0.2 f", area)
}
Copy the code

Running results:

Area calculation failed, radius-20.00 is less than zeroCopy the code

Panic and Recover

The Go language is simple and elegant, so it does not support traditional try... The catch... Finally, because the designers of the Go language believed that mixing exceptions with control structures could easily clutter up the code. It's easy to abuse exceptions and throw an exception for even a minor error. In the Go language, multivalue returns are used to return errors. Do not replace errors with exceptions, and do not use them to control flow. In very rare cases, that is, when a true exception is encountered (e.g0A). To use the Exception handler introduced in Go:defer.panic.recover.Copy the code

Go does not have an exception mechanism like Java, which does not throw exceptions, but instead uses panic and Recover mechanisms. Keep in mind that you should use panic only as a last resort, meaning that there should be nothing, or very little, in your code.

Panic is a built-in function that interrupts the control flow into a Panic flow. When function F calls panic, the execution of function F is interrupted, but the delayed function in F (defer) executes normally, and then F returns to where it was called. Where called, F behaves as if panic was called. The process continues up until all functions called in the Goroutine where panic occurred return, at which point the program exits. Exception errors can be generated directly by calling Panic. It can also result from runtime errors, such as accessing an out-of-bounds array. Recover is a built-in function that allows Goroutine to Recover from a scary process. Recover is only valid in delay functions. In normal execution, a call to recover returns nil and has no other effect. If the current Goroutine is in a panic, the call to Recover captures the input values of Panic and resumes normal execution.

The following function demonstrates how to use Panic in procedures

var user = os.Getenv("USER")
  func init(a) {
    if user == "" {
        panic("no value for $USER")}}Copy the code

The following function checks if the function that takes its argument causes panic when executed:

func throwsPanic(f func(a)) (b bool) {
  defer func(a) {
    if x := recover(a); x ! =nil {
       b = true
    }
  }()
    f() // Execute f, if panic occurs in f, then it can be restored
    return
}
Copy the code

Sample code:

package main

import "fmt"

func main(a)  {
    /* Panic: built-in function that can interrupt the original control flow. Go into a panic. Stop the panic sequence by resuming normal execution and retrieving the error value of the call passed to Panic. Note: Panic can be raised anywhere, but recover is only valid within the defer function */
     testA()
     testB()
     testC()

}

func testA(a)  {
    fmt.Println("\t function A...")}func testB(a)  {
    defer func(a) { // You must declare defer first, otherwise panic exceptions will not be caught
        if r:= recover(a); r ! =nil{ // Err is what panic passed in
            fmt.Println(r,"revocer....")}} ()for i:=0; i<10; i++{ fmt.Println("Function B...", i)
        if i == 5{
            panic("panic....")}}}func testC(a)  {
    fmt.Println("\ t function C...")}Copy the code

Running results:

Function A... Function B... 0 function B... 1 function B... 2 Function B... 3 Function B... 4 function B... 5 panic.... revocer.... Function C...Copy the code