go语言错误处理

简介

目前没有go官方的错误处理规范,通常每个人会有自己的实现,本次梳理几种可行的错误处理实现

方案一: 利用嵌套错误

什么是嵌套错误

Go1.13在errors和fmt标准库包中新增了功能以简化处理包含其他错误的错误。其中最重要的是一个约定: 包含一个错误e2的错误e1可以实现Unwarp方法来返回所包含的底层错误,简单来说,如果e1包装了e2,你可以Unwrap e1来得到e2。

Unwrap 得到的底层错误本身可能也具有Unwrap方法,我们将这种通过重复Unwrap得到的错误序列称为错误链

生成嵌套错误

fmt.Errof是生成嵌套错误最快捷的方法

1
2
var ErrAuth = errors.New("auth error")
var ErrPassword = fmt.Errorf("%w: incorrect password", ErrAuth) //ErrPassword是嵌套错误

判断是否包含某嵌套错误

1
errors.Is(err,ErrAccount) //判断err底层是否包含ErrAccount错误 返回bool Is会检查底层整个错误链

常用函数

1
2
3
4
5
6
7
8
9
10
11
12
// 此函数应该在一个单独的文件中,比如,utils/getlineinfo.go
// 返回示例: -> main.getInfo():/path/to/project/main.go:49
func LineInfo() string {
function := "xxx"
pc, file, line, ok := runtime.Caller(1)
if !ok {
file = "???"
line = 0
}
function = runtime.FuncForPC(pc).Name()
return fmt.Sprintf(" -> %s():%s:%d", function, file, line)
}

示例

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
// LineInfo 返回调用此函数的代码所在函数、文件、行号
// 此函数应该在一个单独的文件中,比如,utils/getlineinfo.go
func LineInfo() string {
function := "xxx"
pc, file, line, ok := runtime.Caller(1)
if !ok {
file = "???"
line = 0
}
function = runtime.FuncForPC(pc).Name()
return fmt.Sprintf(" -> %s():%s:%d", function, file, line)
}
var ErrAuth = errors.New("auth error")
var ErrPassword = fmt.Errorf("%w: incorrect password", ErrAuth)
var ErrAccount = fmt.Errorf("%w: account not exist", ErrAuth)
func login(acc, pwd string) (string, error) {
if acc != "libai" {
return "", ErrAccount
}
if pwd != "123456" {
return "", ErrPassword
}
return fmt.Sprintf("key:AC34cvG-%d", time.Now().Unix()), nil
}
func getInfo(acc, pwd string) (string, error) {
key, err := login(acc, pwd)
err2:=errors.Unwrap(err)
if err != nil { // login 的错误
return "", fmt.Errorf("%w%s", err, LineInfo())
}
// 打开下面的注释就会是 key 过期
//time.Sleep(time.Second)
msg, err := getIntro(key)
if err != nil { // key 错误
return "", fmt.Errorf("%w%s", err, LineInfo())
}
return msg, nil
}
var ErrKey = errors.New("invalid key")
func getIntro(key string) (string, error) {
if key != fmt.Sprintf("key:AC34cvG-%d", time.Now().Unix()) {
return "", ErrKey
}
return "李白,号青莲居士", nil
}
func main() {
info, err := getInfo("libai2", "123456")
if err != nil && errors.Is(err, ErrAuth) { // 无论账号错误还是密码错误,都是认证错误
fmt.Printf("[info]%s\n", err.Error())
} else if err != nil {
fmt.Printf("[error]:%s\n", err.Error())
}
fmt.Println(info)
}

相关资料

代码示例: error_handle/use_runtime/main.go

errors官方文档