How should we handle errors when return it in go?

huangapple go评论75阅读模式
英文:

How should we handle errors when return it in go?

问题

哪个更好?

1
err := MakeFood()
if err != nil{
    return err
}
2
err := MakeFood()
if err != nil{
    logs.Errorf("failed to make food, error=%v",err)
    return err
}
3
err := MakeFood()
if err != nil{
    return fmt.Errorf("failed to make food, error=%w",err)
}
4
var ErrMakeFood = errors.New("failed to make food")

err := MakeFood()
if err != nil{
    return ErrMakeFood // we discard err
}

在我的实践中,return fmt.Errorf("xxx, error=%w",err) 是我最喜欢的方式,当发生错误时,它会创建一个级联的错误字符串并返回。
但是在Go的内置源代码中,return err 是常见且整洁的方式。
有时候我们被建议使用静态错误声明(我给出的示例4)。这是一个名为goerr113的Golang-lint的规则。

英文:

Which is better?

1
err := MakeFood()
if err != nil{
    return err
}
2
err := MakeFood()
if err != nil{
    logs.Errorf("failed to make food, error=%v",err)
    return err
}
3
err := MakeFood()
if err != nil{
    return fmt.Errorf("failed to make food, error=%w",err)
}
4
var ErrMakeFood = errors.New("failed to make food")

err := MakeFood()
if err != nil{
    return ErrMakeFood // we discard err
}

In my practise, return fmt.Errorf("xxx, error=%w",err) is my favorite, which creates a cascaded error string when error happens and return.
But it seems that, in go builtin src code, return err is normal and tidy.
And sometimes we are suggested to use static error declarations(the example 4 I gave). This is the lint rule of a golang-lint named goerr113.

答案1

得分: 2

没有人更好,它们都是不同的,适用于不同的情况。

第一个例子:

err := MakeFood()
if err != nil{
    return err
}

MakeFood已知为其返回的所有错误提供自己的上下文(或者包装并返回错误——"向上冒泡")时,这种方式是可以的;例如,它返回的任何错误都会被包装在"context: failed to make food"的上下文中。

它还适用于其他情况,当不需要提供任何额外的上下文时。

第二个和第三个例子适用于执行某个概念上单一(原子)任务的更大函数的上下文,该任务由多个步骤组成(制作食物是其中之一);在这种情况下,习惯上将每个步骤报告的错误都包装在相同的上下文中,从而导致上下文的"链式",当调用链中的每个调用都添加自己的上下文。
例如,如果我们的更大函数处理食品配送订单,它可能如下所示:

func ProcessFoodDeliveryOrder() error {
  meal, err := MakeFood()
  if err != nil {
    return fmt.Errorf("failed to process order: %w", err)
  }
  err := Deliver(meal, address)
  if err != nil {
    return fmt.Errorf("failed to process order: %w", err)
  }
  return nil
}

关于是否应该使用较新的Go功能,通过使用%w格式化动词来实际"嵌套"错误,与仅提供文本(消息)上下文相对立,这是一个开放的问题:并非所有错误都需要被包装(然后通过errors.Is进行测试或通过errors.As进行处理);仅仅因为你知道它的存在并且看起来很酷,并没有必要使用该功能:它有运行时成本。

使用简单的: %s组合来格式化错误消息更好,就像使用fmt.Errorf("new context: %s", err)一样,因为以这种方式链接错误会产生一个易于理解和阅读的"动作:原因"文本。顺便说一下,这在《The Book》中是推荐的。

后一种方法被称为"哨兵错误"。如果您的包的用户实际上会直接使用或使用errors.Is测试该错误变量,那么使用它是可以的。但是还要考虑到,断言错误的行为可能更好,而不是将其与哨兵变量进行比较。考虑阅读这篇文章并查看net.Error

英文:

No one is better, they are all different and can be appropriate for different cases.

The 1st one,

err := MakeFood()
if err != nil{
    return err
}

is OK when MakeFood is known to provide its own context for all errors it returns (or wraps and returns — "bubbles up"); for instance, any error it ever returns is wrapped in the "failed to make food: " context.

It also fits for other cases when there's no need to provide any additional context.

The second and third are appropriate in the context of a bigger function which performs some conceptually single (atomic) task which consists from multiple steps (and making food is one of such steps); in this case it's customary to wrap errors reported by each of the steps in the same context—leading to context "chaining" when each call in the call chain adds its own context.
For instance, if our bigger function processes a food delivery order, it could look like this:

func ProcessFoodDeliveryOrder() error {
  meal, err := MakeFood()
  if err != nil {
    return fmt.Errorf("failed to process order: %w", err)
  }
  err := Deliver(meal, address)
  if err != nil {
    return fmt.Errorf("failed to process order: %w", err)
  }
  return nil
}

The question of whether one should use the newer Go facility of actually nesting errors by using the %w formatting verb—as opposing to merely providing textual (message) context—is an open one: not all errors need to be wrapped (and then later tested by errors.Is or handled by errors.As); there's no need to use that facility just because you know it exists and looks cool: it has its runtime cost.

The error message is better formatted using plain : %s combo—like with fmt.Errorf("new context: %s", err) because chaning the errors this way produces a trivially understandable and easily read "action: reason" text. BTW it's recommended in The Book.

The latter approach is called "a sentinel error". It's okay to use if the users of your package will actually use such error variable—directly or by testing it using errors.Is.
But also consider that it may be better to assert the behaviour of the errors, not comparing them to a sentinel variable. Consider reading this and look at net.Error.

huangapple
  • 本文由 发表于 2021年9月14日 00:28:21
  • 转载请务必保留本文链接:https://go.coder-hub.com/69166169.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定