Go 从一开始就围绕显式返回错误而不是异常进行构建。这使得代码的可预测性得以提升,避免了像其他语言(例如 Java 或 C++)中的 try/catch 结构所固有的隐藏陷阱。在 Go 中,错误是实现了 Error() 方法的接口,这使得可以创建简单的以及带有上下文的复合/包装错误。
当错误处理不当时(例如通过 _ 忽略错误或不提供额外调试信息包裹错误)或者创建不符合 error 接口的“魔术”错误时,会出现问题。还很重要的是能够从公共函数返回错误并在每个调用中检查它们。
解决方案是使用标准方法:
代码示例:
import ( "errors" "fmt" ) type NotFoundError struct { Resource string } func (e *NotFoundError) Error() string { return fmt.Sprintf("%s not found", e.Resource) } func GetUser(id int) (string, error) { if id != 1 { return "", &NotFoundError{"User"} } return "Steve", nil } func main() { user, err := GetUser(2) if err != nil { if nfe, ok := err.(*NotFoundError); ok { fmt.Println(nfe.Resource, "problem") } else { fmt.Println("error:", err) } } fmt.Println("user:", user) }
关键特点:
在处理错误时可以通过 == 来比较,还是需要使用特殊方法?
最好始终使用 errors.Is() 进行错误包装比较,否则使用 == 可能在错误被包装时失效。
if errors.Is(err, os.ErrNotExist) { // 处理缺少文件的情况 }
实现一个自定义错误类型的结构体是必须的吗,还是使用 errors.New("...") 就足够了?
如果只需要错误的文本,使用 errors.New() 就足够了。如果需要保留上下文(例如资源名称),最好定义一个具有 Error() 方法的 struct。
在函数成功执行的情况下如何正确返回错误?
在成功的情况下始终返回 nil 错误。
return result, nil
负面案例
开发人员编写一个返回没有说明的错误的函数(仅仅是 errors.New("fail"))。在分析日志时无法确定原因。
优点:
缺点:
积极案例
开发人员定义自定义错误类型 NotFoundError,并返回详细描述。
优点:
缺点: