一文悟透备受争议的 Go 语言错误处理
写过 C 的同学知道,C 语言中常常返回整数错误码(errno)来表示函数处理出错,通常用 -1 来表示错误,用 0 表示正确。 而在 Go 中,我们使用 error 类型来表示错误,不过它不再是一个整数类型,是一个接口类型:
它表示那些能用一个字符串就能说清的错误。 我们最常用的就是 errors.New() 函数,非常简单:
使用 New 函数创建出来的 error 类型实际上是 errors 包里未导出的 errorString 类型,它包含唯一的一个字段 s,并且实现了唯一的方法:Error() string。 通常这就够了,它能反映当时“出错了”,但是有些时候我们需要更加具体的信息,例如:
当调用者发现出错的时候,只知道传入了一个负数进来,并不清楚到底传的是什么值。在 Go 里:
它要求返回这个错误的函数要给出具体的“上下文”信息,也就是说,在 Sqrt 函数里,要给出这个负数到底是什么。 所以,如果发现 f 小于 0,应该这样返回错误:
这就用到了 fmt.Errorf 函数,它先将字符串格式化,再调用 errors.New 函数来创建错误。 当我们想知道错误类型,并且打印错误的时候,直接打印 error:
或者:
fmt 包会自动调用 err.Error() 函数来打印字符串。 通常,我们将 error 放到函数返回值的最后一个,没什么好说的,大家都这样做,约定俗成。 参考资料【Tony Bai】这篇文章提到,构造 error 的时候,要求传入的字符串首字母小写,结尾不带标点符号,这是因为我们经常会这样使用返回的 error:
error 的困局
在 Go 语言中,错误处理是非常重要的。它从语言层面要求我们需要明确地处理遇到的错误。而不是像其他语言,类如 Java,使用 try-catch- finally 这种“把戏”。 这就造成代码里 “error” 满天飞,显得非常冗长拖沓。 而为了代码健壮性考虑,对于函数返回的每一个错误,我们都不能忽略它。因为出错的同时,很可能会返回一个 nil 类型的对象。如果不对错误进行判断,那下一行对 nil 对象的操作百分之百会引发一个 panic。 这样,Go 语言中诟病最多的就是它的错误处理方式似乎回到了上古 C 语言时代。
Go authors 之一的 Russ Cox 对于这种观点进行过驳斥:当初选择返回值这种错误处理机制而不是 try-catch,主要是考虑前者适用于大型软件,后者更适合小程序。 在参考资料【Go FAQ】里也提到,try-catch 会让代码变得非常混乱,程序员会倾向将一些常见的错误,例如,failing to open a file,也抛到异常里,这会让错误处理更加冗长繁琐且易出错。 而 Go 语言的多返回值使得返回错误异常简单。对于真正的异常,Go 提供 panic-recover 机制,也使得代码看起来非常简洁。 当然 Russ Cox 也承认 Go 的错误处理机制对于开发人员的确有一定的心智负担。 (编辑:ASP站长网) |