在Go

时间:2017-09-03 10:32:13

标签: go error-handling compiler-errors null

在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

解决这个问题的惯用方法是什么?(或任何有效的方法......)

我实际上需要有关错误的额外字段,因为我有更简单的复杂逻辑构造的详细错误消息等,如果我只是回到“字符串错误”,我基本上必须解析< / em>那些字符串碎片并且逻辑基于它们发生等等,这看起来真的很难看(我的意思是,为什么序列化到字符串信息,你知道你以后需要反序列化...)

3 个答案:

答案 0 :(得分:12)

请勿使用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接口的原因。

利用这一点,不要绕过它。

即使你的代码永远不会改变,每个函数或用例都有一个新的自定义错误类型是不可持续的,并且使你的代码不可读并且无法推理。

答案 1 :(得分:1)

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 {

答案 2 :(得分:-1)

解决问题的惯用方法是返回错误界面。

如果您实际上需要一个不属于错误接口的功能,则应创建一个扩展错误接口的新接口。

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}
}