当 return it in go 时,我们应该如何处理错误?
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)
是我最喜欢的,它会在发生错误时创建级联错误字符串 return.
但似乎,在 go builtin src 代码中,return err
是正常和整洁的。
有时我们建议使用静态错误声明(我给出的示例 4)。这是名为 goerr113 的 golang-lint 的 lint 规则。
没有一个更好,它们都是不同的,可以适用于不同的情况。
第一个,
err := MakeFood()
if err != nil{
return err
}
当已知 MakeFood
为它 returns 的所有错误提供自己的上下文时,
是可以的(或包装和 returns —“冒泡”);例如,任何错误 returns 都包含在“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)
一样,因为以这种方式改变错误会产生一个简单易懂且易于阅读的“action: reason”文本。顺便说一句,在 The Book.
中推荐
后一种方法称为“哨兵错误”。可以使用 如果 你的包的用户实际上 使用 这样的错误变量——直接或通过使用 errors.Is
测试它。
但也要考虑断言错误的行为可能更好,而不是将它们与哨兵变量进行比较。考虑阅读 this and look at net.Error
.
哪个更好?
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)
是我最喜欢的,它会在发生错误时创建级联错误字符串 return.
但似乎,在 go builtin src 代码中,return err
是正常和整洁的。
有时我们建议使用静态错误声明(我给出的示例 4)。这是名为 goerr113 的 golang-lint 的 lint 规则。
没有一个更好,它们都是不同的,可以适用于不同的情况。
第一个,
err := MakeFood()
if err != nil{
return err
}
当已知 MakeFood
为它 returns 的所有错误提供自己的上下文时,
是可以的(或包装和 returns —“冒泡”);例如,任何错误 returns 都包含在“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)
一样,因为以这种方式改变错误会产生一个简单易懂且易于阅读的“action: reason”文本。顺便说一句,在 The Book.
后一种方法称为“哨兵错误”。可以使用 如果 你的包的用户实际上 使用 这样的错误变量——直接或通过使用 errors.Is
测试它。
但也要考虑断言错误的行为可能更好,而不是将它们与哨兵变量进行比较。考虑阅读 this and look at net.Error
.