在 Go 中使用自定义Error
类型(带有额外的字段来捕获一些细节)时,当尝试将nil
作为这种类型的值返回时,我收到编译错误,例如cannot convert nil to type DetailedError
或类似cannot use nil as type DetailedError in return argument
,代码大多如下所示:
type DetailedError struct {
x, y int
}
func (e DetailedError) Error() string {
return fmt.Sprintf("Error occured at (%s,%s)", e.x, e.y)
}
func foo(answer, x, y int) (int, DetailedError) {
if answer == 42 {
return 100, nil //!! cannot use nil as type DetailedError in return argument
}
return 0, DetailedError{x: x, y: y}
}
(全文: https://play.golang.org/p/4i6bmAIbRg)
解决这个问题的惯用方法是什么?(或任何有效的方法...
我实际上需要有关错误的额外字段,因为我有由复杂逻辑从更简单的逻辑等构造的详细错误消息,如果我只是回退到"字符串错误",我基本上必须将这些字符串解析为碎片并基于它们发生逻辑等等,这看起来真的很丑陋(我的意思是, 为什么要序列化为字符串信息,您知道稍后需要反序列化...
不要使用DetailedError
作为返回类型,请始终使用error
:
func foo(answer, x, y int) (int, error) {
if answer == 42 {
return 100, nil //!! cannot use nil as type DetailedError in return argument
}
return 0, DetailedError{x: x, y: y}
}
您的DetailedError
类型满足error
接口这一事实足以使这项工作。 然后,在调用方中,如果您关心额外的字段,请使用类型断言:
value, err := foo(...)
if err != nil {
if detailedErr, ok := err.(DetailedError); ok {
// Do something with the detailed error values
} else {
// It's some other error type, behave accordingly
}
}
不退货DetailedError
的原因:
现在看起来可能无关紧要,但将来您的代码可能会扩展以包含其他错误检查:
func foo(answer, x, y int) (int, error) {
cache, err := fetchFromCache(answer, x, y)
if err != nil {
return 0, fmt.Errorf("Failed to read cache: %s", err)
}
// ...
}
其他错误类型不会是DetailedError
类型,因此您必须返回error
。
此外,您的方法可能会被不知道或不关心DetailedError
类型的其他调用方使用:
func fooWrapper(answer, x, y int) (int, error) {
// Do something before calling foo
result, err := foo(answer, x, y)
if err != nil {
return 0, err
}
// Do something after calling foo
return result, nil
}
期望函数的每个调用者都理解自定义错误类型是不合理的——这正是 Go 中存在接口,尤其是error
接口的原因。
利用这一点,不要规避它。
即使您的代码永远不会更改,为每个函数或用例使用新的自定义错误类型也是不可持续的,并且会使您的代码不可读且无法推理。
DetailedError struct
零值不是nil
而是DetailedError{}
。您可以返回error
接口而不是DetailedError
func foo(answer, x, y int) (int, error) {
或使用指针
func foo(answer, x, y int) (int, *DetailedError) {
...
//and
func (e *DetailedError) Error() string {
解决问题的惯用方法是返回错误接口。
如果实际需要不属于错误接口的函数,则应创建一个扩展错误接口的新接口。
type DetailedErrorInterface interface {
error
GetX() int //need to implement
GetY() int //need to implement
}
然后,您必须更改函数以返回此接口:
func foo(answer, x, y int) (int, DetailedErrorInterface) {
if answer == 42 {
return 100, nil
}
return 0, DetailedError{x: x, y: y}
}