golang development: use of Error

Error is the most basic and important part of Go language development. It is basically the same as the role of try catch in other languages. Think about the fact that in PHP JAVA development, try catch can't be used or used flexibly, so you can't perceive what errors occur in program operation. It's a particularly terrible thing.

Error basis

The error type in Golang is the most basic interface, which defines an Error() method

type error interface {
	Error() string
}

The most commonly used is this

errors.New("error")

In Golang, errors New is defined this way

func New(text string) error {
	return &errorString{text}
}

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

func (e *errorString) Error() string {
	return e.s
}

In fact, an errorString structure is returned, which implements the Error() method, so the error interface is implemented

Let's see how Error is used in project development?

1. Define the Error variable

Many errors may be returned in a piece of code. How can I judge which error is?
Is that right

var ERR_MSG = "error"
if err.Error() == ERR_MSG

In this case, if the error descriptions of multiple third-party class libraries and their own projects are consistent, they cannot be compared. In fact, this should not be the case.
Let's see how orm is defined in beego. From the above basis, we know errors New returns a pointer to errorString

var (
	ErrTxHasBegan    = errors.New("<Ormer.Begin> transaction already begin")
	ErrTxDone        = errors.New("<Ormer.Commit/Rollback> transaction not begin")
	ErrMultiRows     = errors.New("<QuerySeter> return multi rows")
	ErrNoRows        = errors.New("<QuerySeter> no row found")
	ErrStmtClosed    = errors.New("<QuerySeter> stmt already closed")
	ErrArgs          = errors.New("<Ormer> args error may be empty")
	ErrNotImplement  = errors.New("have not implement")
)

In fact, they all use pointers to judge

See how to use it. Here is the pseudo code

err := this.QueryTable(this.table).Filter("id", id).One(data)
if err != nil && err != orm.ErrNoRows {
	return err
}
return nil

In fact, this method is often used in Golang source code or third-party class libraries. The disadvantage is coupling. When using a third-party class library, the caller needs to know the error types in its code, and also needs to use these error type variables for comparison in the project. It is difficult for developers who use it for the first time to think of using it like this.

2. Customize your own Error

In the past, PHP project Exception defined its own error code.
In Golang, we can also define our own Error type, and then use assertions to determine which Error to obtain more Error data. Take a look at the following example code to understand the simple use of custom Error

type SelfError struct {
	Code int
	Err error
}

func (this *SelfError) Error() string {
	return this.Err.Error()
}
func (this *SelfError) GetCode() int {
	return this.Code
}

func OpenFile(name string) error {
	err := os.Rename("/tmp/test","/tmp/test1")
	if err != nil {
		return &SelfError{-1001, err}
	}
	return nil
}

func main() {
	err := OpenFile("test")
	switch erro := err.(type) {
	case nil:
		fmt.Println("success")
	case *SelfError:
		fmt.Println(erro.Error(),erro.Code)
	case error:
		fmt.Println(erro.Error())
	}
}

Another usage is to judge whether the error type is user-defined. If so, return the user-defined attribute

func main() {
	err := OpenFile("test")
	serr, ok := err.(*SelfError)
	if ok {
		fmt.Println(serr.GetCode())
	}
}

It can be seen that assertions are used to determine whether the error is a user-defined error. If so, the user-defined error's own properties and methods are used.

Coupling, the caller needs to use switch or assertion to use the custom Error attribute.

3. Use of wrap errors

The use of wrap errors should be one of the most used in the project to deal with errors, which can be easily added to the context of use.
Wrap Errors, as the name suggests, is to wrap error layer by layer. The outermost layer gets a stack information of error. According to the stack information, you can always track the first calling code that causes error.
This package is required

github.com/pkg/errors

Take a look at the code example

package main

import (
	"fmt"
	"github.com/pkg/errors"
	"os"
)

func ModelFile() error {
	err := os.Rename("/tmp/test","/tmp/test1")
	if err != nil {
		return errors.Wrap(err, "model_rename_fail")
	}
	return nil
}

func LogicFile() error {
	err := ModelFile()
	if err != nil {
		return errors.Wrap(err, "logic_rename_fail")
	}
	return nil
}

func main() {
	err := LogicFile()
	if err != nil {
		fmt.Printf("error:%v", errors.Cause(err))
		fmt.Printf("%+v", err)
	}
}

Look at the stack of execution results

error:rename /tmp/test /tmp/test1: no such file or directoryrename /tmp/test /tmp/test1: no such file or directory
model_rename_fail
main.ModelFile
        /data/www/go/src/test1/main.go:12
main.LogicFile
        /data/www/go/src/test1/main.go:18
main.main
        /data/www/go/src/test1/main.go:26
runtime.main
        /usr/local/go/src/runtime/proc.go:203
runtime.goexit
        /usr/local/go/src/runtime/asm_amd64.s:1357
logic_rename_fail
main.LogicFile
        /data/www/go/src/test1/main.go:20
main.main
        /data/www/go/src/test1/main.go:26
runtime.main
        /usr/local/go/src/runtime/proc.go:203
runtime.goexit
        /usr/local/go/src/runtime/asm_amd64.s:1357

Simple rules to use

With so many usage methods, which one should be used? The general suggestion should be like this

  1. When it is necessary to compare the wrong types, it must be used in the first way, and there is no better way at present
  2. If you need to add your own project error code or some complex context, you may need to use the second custom error type
  3. If you need to rely on a third-party class library, which may be unstable, wrap error has obvious advantages. You can print the record stack for easy positioning.
  4. For some common simple projects, you only need to record the context where the error is triggered, print a log, and directly return error, which is the simplest and most convenient.

Keywords: Go

Added by am_25 on Sat, 15 Jan 2022 22:23:06 +0200