The original error

Friends who are familiar with go development know that display is recommended to handle errors in go design. When used, the operation succeeds by suggesting that the function return an error value and comparing the returned error variable with nil.

Error in GO is defined as follows:

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

The original error is defined as interface.

It can be seen that the original error is very simple, just MSG, but for general business development, we still need code information. According to the interface design of GO, we can implement a custom Error as long as we implement the Error() string method.

Custom error

package errors

import "fmt"

type myErr struct {
   code int
   msg  string
}

func (e myErr) Error(a) string {
   return fmt.Sprintf("code:%d,msg:%v", e.code, e.msg)
}

func New(code int, msg string) error {
   return myErr{
      code: code,
      msg:  msg,
   }
}

func GetCode(err error) int {
   if e, ok := err.(myErr); ok {
      return e.code
   }
   return - 1
}

func GetMsg(err error) string {
   if e, ok := err.(myErr); ok {
      return e.msg
   }
   return ""
}
Copy the code

By customizing a struct and implementing the Error() string method, we customize an Error with code.

It is important to note that we declare myErr to start with a lower case, so that it is not declared directly outside the package. Only New(code int, MSG string) is allowed to get an error.

What would be the problem if it could be declared directly outside the package? This is the pit I’m going to introduce.

Consider the following example:

package main

import "fmt"

func main(a) {
   err := HelloWorld()
   fmt.Println(err == nil) // The output is false
}

func HelloWorld(a) error {
   var err *MyErr
   //do something
   return err
}

type MyErr struct {
   code int
   msg  string
}

func (e *MyErr) Error(a) string {
   return fmt.Sprintf("code:%d,msg:%v", e.code, e.msg)
}
Copy the code

We would be surprised to see err == nil output false.

All this is caused by the interface design of GO, which is designed in two parts:

  • type
  • value

Where value is represented by an arbitrary concrete value, called the Dynamic value of interface; Type corresponds to the type of value (i.e., dynamic type). For example, for var a int = 3, when we assign a to interface, the interface is stored with (int, 3).

(nil, nil); (nil, nil); (nil, nil);

So back to the example above, when var err *MyErr is assigned to interface, interface’s store is (*MyErr,nil). The result of nil is definitely false.

So when we customize an error, we want to avoid error being declared directly as much as possible. Generate an error by providing a function form.

One is to avoid the above problems, and two is to mask the implementation details, so it can be better extended.