错误处理
字数
704 字
阅读时间
4 分钟
Go 中的错误是一个简单的接口类型:
go
type error interface {
Error() string
}编写任何库的时候,就可以通过实现这个接口,提供错误的上下文信息。例如 os.Open 返回的错误如下:
go
// PathError records an error and the operation and
// file path that caused it.
type PathError struct {
Op string // "open", "unlink", etc.
Path string // The associated file.
Err error // Returned by the system call.
}
func (e *PathError) Error() string {
return e.Op + " " + e.Path + ": " + e.Err.Error()
}这个错误字符串类似这样:“open /etc/passwx: no such file or directory”
调用者如果关心错误的具体类型,可通过类型断言来获取:
go
if err, ok := err.(*os.PathError); ok && err.Err == syscall.ENOSPC {
deleteTempFiles()
}错误是值(Errors are values)
设计接口时,也可以不直接返回错误,而是通过一个方法去读取错误:
go
scanner := bufio.NewScanner(input)
for scanner.Scan() {
token := scanner.Text()
// process token
}
if err := scanner.Err(); err != nil {
// process the error
}包装错误(Wrapping errors)
用 fmt.Errorf("more context info: %w", err) 来包装错误到一个错误链,而不是直接拼接字符串或errors.New(fmt.Sprintf("%v", err)) 。这样可以用 errors.Unwrap 来拆解错误链。
go
var ErrOutOfTea = fmt.Errorf("no more tea available")
var ErrPower = fmt.Errorf("can't boil water")
func makeTea(arg int) error {
if arg == 2 {
return ErrOutOfTea
}
if arg == 4 {
return fmt.Errorf("making tea: %w", ErrPower)
}
return nil
}
func main() {
for i := range 5 {
if err := makeTea(i); err != nil {
if errors.Is(err, ErrOutOfTea) {
fmt.Println("We should buy new tea!")
} else if errors.Is(err, ErrPower) {
fmt.Println("Now it is dark.")
} else {
fmt.Printf("unknown error: %s\n", err)
}
continue
}
fmt.Println("Tea is ready!")
}
}返回错误(Returning errors)
- 避免具体错误类型:导出函数返回
error而非具体类型(如*os.PathError),防止接口包装导致的nil错误。
带内错误
带内错误指通过函数的正常返回值(如 -1、""、null)来传递错误或缺失结果的方式,常见于 C 等语言
- Go 中不鼓励使用带内错误,应使用多返回值返回额外的错误。
错误信息(Error strings)
- 格式:不要首字母大写,也不要结尾标点(如
fmt.Errorf("something bad happened"))。 - 日志场景:完整消息需首字母大写(如
log.Errorf("Operation failed: %v", err))。
错误处理逻辑
- 不忽略错误:尽量不要用
_丢弃错误,必要时注释说明(如n, _ := b.Write(p) // 文档声明永不失败)。 - 提前处理:错误处理代码前置,避免深层嵌套(使用
if err != nil { return }而非else块)。
不要 Panic
- 避免使用
panic处理常规错误,使用error和多返回值。
打印错误
log.V2.Error().with(ctx).Error(err).Emit() 使用专用的方法打印
