An overview of the

There is often a need in development to define error codes that uniquely identify specific error messages. You also need to set the specific description of each error. In HTTP, 200 means “OK” and 404 means “Not Found”. On Linux, ENOENT is 2, which means “No such file or directory”. The Errno type is defined in the Syscall package to represent the system error code. It is very easy to use and is recommended to check it out.

Each time you define an error code, you need to add a description. And the descriptive information is often forgotten. This article introduces the Go Generate + Stringer toolchain to gracefully solve this problem.

Golang Tools, by the way, is an official toolset and a treasure trove of Gophers tools worth exploring. See Github for more information. It has a wealth of development AIDS that all Go development plug-ins need to support. For example, the Goimports tool automatically imports used packages and removes unused packages. Gorename is used to rename identifiers.

The Stringer tool that we’re going to use today is also in the Tools kit.

The traditional way

Define error code:

package errcode

import "fmt"ERR_CODE_OK = 0 // OK ERR_CODE_INVALID_PARAMS = 1 // Invalid parameter ERR_CODE_TIMEOUT = 2 // timeout //... Var mapErrDesc = map[int]string {ERR_CODE_OK:"OK",
    ERR_CODE_INVALID_PARAMS: "Invalid parameter",
    ERR_CODE_TIMEOUT: "Timeout", / /... Func GetDescription(errCode int) string {func GetDescription(errCode int) string {if desc, exist := mapErrDesc[errCode]; exist {
        return desc
    }
    
    return fmt.Sprintf("error code: %d", errCode)
}
Copy the code

Using error code:

package main

import (
    "github.com/darjun/errcode"
)

func main() {code := errcode.err_code_invalid_params fmT.println (code, errcode.getDescription (errcode)) // Output: 1 Invalid parameter}Copy the code

For ease of use, we can define a new type for the error code, and then define the String() method for that type, so that we don’t have to call the GetDescription function manually. The modification is as follows:

typeErrCode int const (ERR_CODE_OK ErrCode = 0 // OK ERR_CODE_INVALID_PARAMS ErrCode = 1 // Invalid parameter ERR_CODE_TIMEOUT ErrCode = 2 // Timeout) func (e ErrCode) String() String {return GetDescription(e)
}
Copy the code

There is no need to call the GetDescription function externally, and therefore no need to export. Change the function name to getDescription to not export.

What’s wrong with this approach?

Every time you add an error code, you need to change mapErrDesc, and sometimes you forget it. In addition, error descriptions appear in both comments and mapErrDesc. Can we just comment and use the tool to automatically generate the code we want?

Let’s see how to solve this problem with go generate + Stringer.

go generate

Go Generate is a tool that comes with Go. Run the go generate command. Go Generate works using comments in the source code. Format is as follows:

//go:generate command arg1 arg2
Copy the code

Executing the go generate command in the same directory will automatically run command arg1 and arg2. Command can be any command in the PATH and is widely used. The official website provides several examples, see the documentation.

The stringer command generates a String method for a given type.

The installationstringer

Stringer doesn’t come with Go and needs to be installed manually. To install, run the following command:

$ go get golang.org/x/tools/cmd/stringer
Copy the code

Orders from above need to go over the wall. It can also be installed via an image on Github. Assuming you already have the Go development environment configured, install it as follows:

$ git clone https://github.com/golang/tools/ $GOPATH/src/golang.org/x/tools
$ go install golang.org/x/tools/cmd/stringer
Copy the code

The installed Stringer command is in the $GOPATH/bin directory, which is strongly recommended to be added to the system PATH.

use

Stringer has two modes, the default is to generate string descriptions based on variable/constant names. Let’s add a comment to the constant definition:

//go:generate stringer -type ErrCode
Copy the code

Option -type Specifies the name of the type used by the stringer command.

Then execute in the same directory:

$ go generate
Copy the code

A file errcode_string.go is generated in the same directory. The file name format is type name lowercase _string.go. You can also specify the output file name with the -output option. For example, here is the output file name code_string.go:

//go:generate stringer -type ErrCode -output code_string.go
Copy the code

Let’s take a look at the contents of this file:

// Code generated by "stringer -type ErrCode -output errcode_string.go"; DO NOT EDIT.

package errcode

import "strconv"

const _ErrCode_name = "ERR_CODE_OKERR_CODE_INVALID_PARAMSERR_CODE_TIMEOUT"var _ErrCode_index = [...] uint8{0, 11, 34, 50} func (i ErrCode) String() string {if i < 0 || i >= ErrCode(len(_ErrCode_index)-1) {
		return "ErrCode(" + strconv.FormatInt(int64(i), 10) + ")"
	}
	return _ErrCode_name[_ErrCode_index[i]:_ErrCode_index[i+1]]
}
Copy the code

The generated code has been optimized to reduce the number of string objects.

Err_code_invalid_params.string () returns ERR_CODE_INVALID_PARAMS. In some contexts you don’t even need to call the String() method yourself, such as fmt.println. Because ErrCode implements FMT. Stringer, it is automatically called in some contexts.

This removes the global variable mapErrDesc and the getDescription function from the errcode.go file.

But what we would prefer is to return the following comment as a description of the error. This requires stringer’s -linecomment option. Modify go:generate as follows:

//go:generate stringer -type ErrCode -linecomment -output code_string.go
Copy the code

Then, execute the go generate command. The code_string.go generated is different from the previous code_string.go, as follows:

const _ErrCode_name = "OK invalid parameter timeout"var _ErrCode_index = [...] uint8{0, 2, 14, 20}Copy the code

You can see that the error message is indeed generated through the comments.

Note:

  1. go:generateYou can only use//Comments. Comments must be at the beginning of a line, not preceded by Spaces and//withgo:generateNo space between!!
  2. go:generateThis can be in any Go source file, preferably where the type is defined.

automation

Some people say, well, it’s still a hassle: I have to run go generate every time, and I forgot what to do. I’m lazy and don’t want to tap this command manually. No problem, not lazy is not a good programmer. We can execute this command in a build project script, such as in a makefile:

all:
    go generate && go build .
Copy the code

This makes it possible to execute go generate and compile the project simultaneously. Perfect!

Of course, the above command is relatively simple, in a real project you may need to deal with the directory.

The code is on Github.

Refer to the link

  1. go blog – generate
  2. Go the generate documentation

Personal home page