在Go中返回nil或自定义错误

Neu*_*onQ 6 error-handling null compiler-errors go

Error在Go中使用自定义类型(使用额外的字段来捕获一些细节)时,当尝试返回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}
}
Run Code Online (Sandbox Code Playgroud)

(完整片段:https://play.golang.org/p/4i6bmAIbRg)

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

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

Fli*_*mzy 13

不要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}
}
Run Code Online (Sandbox Code Playgroud)

您的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
    }
}
Run Code Online (Sandbox Code Playgroud)

不退货的原因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)
    }
    // ... 
}
Run Code Online (Sandbox Code Playgroud)

其他错误类型不是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
}
Run Code Online (Sandbox Code Playgroud)

期望函数的每个调用者都能理解自定义错误类型是不合理的 - 这正是errorGo中存在接口,特别是接口的原因.

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

即使您的代码永远不会更改,为每个函数或用例创建新的自定义错误类型也是不可持续的,并且使您的代码无法读取且无法推理.