【问题标题】:Idiomatic way of returning a not-found error in Golang?在 Golang 中返回未找到错误的惯用方式?
【发布时间】:2022-02-02 03:03:23
【问题描述】:

我在 Go 中有一个带有这个签名的函数:

func GetAccount(ctx context.Context, id uuid.UUID) (*Account, error)

如果出现内部错误(例如数据库查询由于某种原因失败),它会返回一个错误,但如果找不到帐户,我不确定应该返回什么。我可以想到两种不同的方法:

  1. 只返回一个 nil 帐户,如果没有找到帐户则返回 nil 错误
  2. 返回自定义错误类型,如下所示:
type accountNotFoundErr struct {
    id uuid.UUID
}

func (err accountNotFoundErr) Error() string {
    return fmt.Sprintf("account not found for user: %v", err.id)
}

func IsAccountNotFoundErr(err error) bool {
    _, ok := err.(accountNotFoundErr)
    return ok
}

func GetAccount(ctx context.Context, id uuid.UUID) (*Account, error) {
    // if the account is not found
    return nil, accountNotFoundErr{id}
}

我喜欢第一个,因为它很简单,但我不经常看到 Go 代码在错误为非 nil 时返回 nil 结果。我认为期望是,如果错误为零,则结果是有效的。第二种方法解决了这个问题,但对调用者来说也有点复杂。

在 Go 中处理此类案例的惯用方法是什么?

【问题讨论】:

  • 您应该确定找不到帐户是否是错误。这可能因功能/用例而异。例如。如果在登录期间用户指定了用户名,则查找该帐户可能很容易不存在(用户可能输入错误的用户名)。但是如果你想查找一个已登录的账号,如果他/她已经登录,则该账号必须存在,这种情况下找不到账号就意味着错误。不管是什么情况,记录你的函数的行为总是好的。

标签: go


【解决方案1】:

我已经阅读了很多关于 go 自定义错误的帖子。他们中的大多数都创建了自己的结构来实现错误接口。

我发现使用这种方法的问题是我无法轻松检查错误是否属于某个类型。同样的方式,您也许可以检查一些标准库错误,例如if error == EOF

因此,我最喜欢的做法是使用 erros.New 创建一个简单的 var。

var ErrNotFound = errors.New("Resource was not found")

func main() {
    err := raise()
    if err == ErrNotFound {
        fmt.Println("impossibru")
        return
    }
    if err != nil {
        fmt.Println("unexpected error")
        return
    }
}

func raise() error {
    return ErrNotFound
}

https://play.golang.com/p/s0ZQfsdLqxB

正如@Gavin 在 cmets 中指出的那样,如果您想通过用fmt.Errorf 包装错误来为错误提供更多上下文,则需要使用errors.Is 来检查特定错误是否被包装。

var ErrNotFound = errors.New("Resource was not found")

func main() {
    err := raise(42)
    if errors.Is(err, ErrNotFound) {
        fmt.Println(err)
        return
    }
    if err != nil {
        fmt.Println("unexpected error")
        return
    }
}

func raise(id int) error {
    return fmt.Errorf("id %d does not exist, error: %w", id, ErrNotFound)
}

https://play.golang.com/p/hSrkb1Xp4Hn

【讨论】:

  • 如果错误被包装,这将失败。相反,您应该使用if errors.Is(err, ErrNotFound)play.golang.com/p/536y_bh7_jy
  • 我们应该对每个错误都这样做吗?还是只有那些可能被包装的?因为我看到的大部分代码都没有使用errors.Is。 IE。 if err := server.ListenAndServe(); err != http.ErrServerClosed {}.
  • 但是你的例子很好。我从来没有使用过这种技术。有时添加更多上下文是有意义的。
  • 您提供的ListenAndServe() 示例并不关心返回的错误是什么,只关心存在错误。如果您对特定错误感兴趣,那么是的,您应该几乎始终使用errors.Is
  • @Gavin,好的,谢谢你的提示。我已将其添加到我的答案中。
猜你喜欢
  • 1970-01-01
  • 2013-10-27
  • 2018-04-18
  • 1970-01-01
  • 2014-04-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-11-18
相关资源
最近更新 更多