gpt4 book ai didi

pointers - 如何在不导致 nil 值问题的情况下返回自定义错误

转载 作者:IT王子 更新时间:2023-10-29 01:34:48 26 4
gpt4 key购买 nike

我已经实现了一个自定义错误类型,并且确实得到了关于 nil 值的奇怪行为。当我将自定义错误作为标准错误接口(interface)传递时,它永远不会被识别为 nil,即使自定义错误返回为 nil。

看看这个小测试程序:

package main

import (
"fmt"
"strconv"
)

type CustomError struct {
Code int
}

func (e *CustomError) Error() string {
return strconv.Itoa(e.Code)
}

func FailCustom(dofail bool) *CustomError {
if dofail {
return &CustomError{Code: 42}
} else {
return nil
}
}

func WrapFailCustom(dofail bool) error {
return FailCustom(dofail)
}

func main() {
err := WrapFailCustom(false)
if err == nil {
fmt.Println("err is nil")
} else {
fmt.Println("err is not nil")
}
}

同样在 Playground 上:https://play.golang.org/p/7bqeDw5B5fU

这实际上输出“err is not nil”

我本以为 *CustomError 类型的 nil 值会隐式转换为 error 类型的 nil 值。谁能向我解释为什么不是这种情况以及如何正确传播自定义错误类型的 nil 值?

编辑:可以找到对此的解释 here正如伊恩·邓肯所指出的

为了进一步探讨这个问题,让我们考虑对 WrapFailCustom 进行以下修改:

func WrapFailCustom(dofail bool) error {
err := FailCustom(dofail)
if err == nil {
return nil
} else {
return err
}
}

这实际上返回“err is nil”:https://play.golang.org/p/mEKJFyk5zqf

我确实觉得依赖这个作为解决方案真的很糟糕,因为在使用会吐出我的自定义错误的函数时很容易忘记它。有没有更好的方法来制作自定义错误来防止这种“歧义”的发生?一直使用基本错误类型的明显解决方案,对于使用 WrapFailCustom 等函数的代码来说似乎真的很不方便,所以我想避免这种情况......

最佳答案

有关背景,请参阅 Hiding nil values, understanding why golang fails here ;和 Go FAQ: Why is my nil error value not equal to nil?

Go 要求您明确类型和转换(例如,您不能将类型 int32 的值添加到类型 int 的值),但是接口(interface)转换自动创建接口(interface)值是这个规则的异常(exception)。每当需要接口(interface)类型的值时,您可以使用其类型实现(满足)接口(interface)类型的任何值,接口(interface)值将自动为您创建。

你的功能:

func WrapFailCustom(dofail bool) error {
return FailCustom(dofail)
}

WrapFailCustom() 有一个返回类型error,你尝试返回 FailCustom() 函数的结果,其返回类型是 *CustomErrorerror 不同!这应该引起警惕!

什么/如何返回? error 类型的接口(interface)值将被自动创建! *CustomError 实现了 error,所以一切都很好,但是正如您所经历的,如果指针是 nil,这种隐式值包装不会导致一个接口(interface)值是 nil,而是一个非 nil 接口(interface)值包装了值 nil 和类型 *CustomError.

解决方案?

解决根本原因

FailCustom() 有一个不同于error 的返回类型真的合理/它有任何值(value)吗?如果没有,最简单的方法是在“问题”的来源处处理它:

func FailCustom(dofail bool) error {
if dofail {
return &CustomError{Code: 42}
}
return nil
}

然后您所有的问题都会消失。如果您按照“Go 方式”使用 error 类型返回错误,这就足够了并且令人满意。您甚至不再需要 WrapFailCustom() 函数。

WrapFailCustom()

中手动计算它

如果您确实需要 FailCustom() 返回自定义 *CustomError 类型,那么您需要在 WrapFailCustom()< 中“手动”考虑它。我会这样写:

func WrapFailCustom(dofail bool) error {
if customErr := FailCustom(dofail); customErr != nil {
return customErr
}
return nil
}

(请注意,我故意使用不同的 customErr 名称而不是 err,表明它不是 error 类型,应该注意采取如何将其转换为error。)

使用作为接口(interface)类型的自定义错误类型

如果您想/需要使用您的自定义错误类型,另一种好方法是创建一个接口(interface)类型来描述它包含的“额外”功能:

type CustomErr interface {
Error // Embed error interface
Code() int
}

然后我们还需要实现这个Code()方法:

func (e *CustomError) Code() int { return e.Code }

这个有什么用?

根本原因将通过返回此接口(interface) 类型(并且不是 指针)的值来处理:

func FailCustom(dofail bool) CustomErr {
if dofail {
return &CustomError{Code: 42}
}
return nil
}

隐式接口(interface)值将在 FailCustom() 中创建。

此外,WrapFailCustom() 变得不必要/无用。 FailCustom() 返回一个既是错误又是代码的值,您可以使用它的代码()<从中获取代码/ 方法。返回值是一个接口(interface)值,一个error,你可以在需要error值的地方使用它。具体类型 CustomError 甚至可以不导出(隐藏)。

与此方法相关,查看 Dave Cheney: Don’t just check errors, handle them gracefully ,尤其是标题为:Assert errors for behavior, not type 的部分。

关于pointers - 如何在不导致 nil 值问题的情况下返回自定义错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49768691/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com