golang 的错误处理,我之前也吐槽,但从 1.13 开始吧就挺好用了。 之前吐槽点:
- 如果底层函数出错,只在上层打印错误信息,会丢失调用栈,不知道最开始的错误发生在哪里。
- 如果通过字符串追加的方式,加入调用栈信息,那么错误类型会丢失,无法像 if err == io.EOF 这样判断是什么错误。
现在已经不是问题了。
// 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 ErrAccount = fmt.Errorf("%w: account not exist", ErrAuth)
var ErrPassword = fmt.Errorf("%w: incorrect password", 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)
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("libai", "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)
}