Golang 的 generate stringer

普通代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
package main

import "fmt"

type Code int

const (
    OK           Code = 1
    BadRequest   Code = 2
    InvalidParam Code = 3
)

func main() {
    fmt.Println(BadRequest)
}

运行结果:

1
2
3
4
$ ls
go.mod  go.sum  main.go
$ go run .
2

Codeint类型,正常情况下,输出是整形数字。

使用 go generate 和 stringer

下面看下使用go generate和官方的stringer是什么效果。

安装

1
go install golang.org/x/tools/cmd/stringer

添加标记

在代码的任意地方添加一行 //go:generate stringer -type=Code

注意,//后面不能有空格

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
package main

import "fmt"

//go:generate stringer -type=Code

type Code int

const (
    OK           Code = 1
    BadRequest   Code = 2
    InvalidParam Code = 3
)

func main() {
    fmt.Println(BadRequest)
}

运行结果:

1
2
3
4
5
$ go generate
$ ls
code_string.go go.mod         go.sum         main.go
$ go run .
BadRequest

执行go generate后,会生成一个 code_string.go 的文件,其内容如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// Code generated by "stringer -type=Code"; DO NOT EDIT.

package main

import "strconv"

func _() {
    // An "invalid array index" compiler error signifies that the constant values have changed.
    // Re-run the stringer command to generate them again.
    var x [1]struct{}
    _ = x[OK-1]
    _ = x[BadRequest-2]
    _ = x[InvalidParam-3]
}

const _Code_name = "OKBadRequestInvalidParam"

var _Code_index = [...]uint8{0, 2, 12, 24}

func (i Code) String() string {
    i -= 1
    if i < 0 || i >= Code(len(_Code_index)-1) {
        return "Code(" + strconv.FormatInt(int64(i+1), 10) + ")"
    }
    return _Code_name[_Code_index[i]:_Code_index[i+1]]
}

当代码中再次打印 Code 值的时候,输出不再是整形数字,而是变成了字符串,并且是对应的变量名称。

更多用法

如果再增加一个-linecomment参数,变成//go:generate stringer -type=Code -linecomment,表示打印的时候从注释取值,具体效果如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
package main

import "fmt"

//go:generate stringer -type=Code -linecomment

type Code int

const (
    OK           Code = 1 // 正常OK
    BadRequest   Code = 2 // 请求错误
    InvalidParam Code = 3
)

func main() {
    fmt.Println(BadRequest)
    fmt.Println(InvalidParam)
}

执行go generate后生成的 code_string.go 内容如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// Code generated by "stringer -type=Code -linecomment"; DO NOT EDIT.

package main

import "strconv"

func _() {
    // An "invalid array index" compiler error signifies that the constant values have changed.
    // Re-run the stringer command to generate them again.
    var x [1]struct{}
    _ = x[OK-1]
    _ = x[BadRequest-2]
    _ = x[InvalidParam-3]
}

const _Code_name = "正常OK请求错误InvalidParam"

var _Code_index = [...]uint8{0, 8, 20, 32}

func (i Code) String() string {
    i -= 1
    if i < 0 || i >= Code(len(_Code_index)-1) {
        return "Code(" + strconv.FormatInt(int64(i+1), 10) + ")"
    }
    return _Code_name[_Code_index[i]:_Code_index[i+1]]
}

运行结果:

1
2
3
$ go run .
请求错误
InvalidParam

可见,加了-linecomment参数后,如果变量有注释,则打印内容默认为注释,否则默认为变量名称。

更多参数:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
$ stringer -h
Usage of stringer:
    stringer [flags] -type T [directory]
    stringer [flags] -type T files... # Must be a single package
For more information, see:
    https://pkg.go.dev/golang.org/x/tools/cmd/stringer
Flags:
  -linecomment
        use line comment text as printed text when present
  -output string
        output file name; default srcdir/<type>_string.go
  -tags string
        comma-separated list of build tags to apply
  -trimprefix prefix
        trim the prefix from the generated constant names
  -type string
        comma-separated list of type names; must be set