Go语言返回错误时的处理方法对比:四种常见写法哪个更优?
Go 语言的错误处理没有绝对最优的写法,完全取决于你当前代码的场景和错误处理的需求,四种写法的优劣势和适用场景分别如下:
各写法分析
- 写法1:直接返回原始错误
err := MakeFood() if err != nil { return err }
优势:写法最简洁,无额外性能开销,Go 标准库中大量使用该写法。
适用场景:当前逻辑层不需要给错误附加额外上下文,且上层调用方明确知道 MakeFood 可能返回的错误类型,不需要额外信息即可完成错误处理的场景。
劣势:如果调用链路较长,上层拿到错误后无法定位错误发生的层级,排查问题难度高。
- 写法2:打印日志后返回原始错误
err := MakeFood() if err != nil { logs.Errorf("failed to make food, error=%v", err) return err }
优势:错误发生时当场记录日志,不会遗漏错误现场,方便第一时间定位出错位置。
适用场景:当前是顶层逻辑(如HTTP接口Handler、异步任务执行入口),错误不会再向上层抛出的场景。
劣势:如果多层逻辑都采用该写法,会产生大量重复的错误日志,既浪费存储资源也会干扰问题排查,普通业务中间层不建议使用。
- 写法3:用
fmt.Errorf加%w包装错误
err := MakeFood() if err != nil { return fmt.Errorf("failed to make food, error=%w", err) }
优势:可以给错误叠加当前层的上下文信息,最终打印完整错误链路时可以清晰看到调用路径,同时通过%w包装的错误保留了原始错误信息,上层依然可以用errors.Is、errors.As判断原始错误类型做分支处理,是通用性最强的写法。
适用场景:绝大多数业务中间层场景,兼顾问题排查的便利性和上层判断错误类型的需求,普通业务开发优先选择该写法。
劣势:存在极少量的性能开销,普通业务场景完全可以忽略。
- 写法4:返回预定义静态错误,丢弃原始错误
var ErrMakeFood = errors.New("failed to make food") err := MakeFood() if err != nil { return ErrMakeFood // we discard err }
该写法是goerr113 lint规则的推荐写法。
优势:对外暴露的错误是固定的枚举值,上层调用方可以直接通过errors.Is(err, ErrMakeFood)做错误类型判断,分支处理逻辑非常稳定,不会因为底层实现的错误变化影响上层逻辑。
适用场景:开发对外暴露的SDK、公共库,需要对外屏蔽底层实现细节,只暴露约定好的错误枚举的场景。
劣势:直接丢弃了原始错误信息,排查问题时无法定位底层出错的根本原因,普通业务内部代码不建议使用。
日常开发选择优先级
- 公共SDK/对外暴露的库代码:优先选择预定义静态错误的写法,可自定义错误类型同时包装原始错误,避免丢失排查信息。
- 普通业务中间层代码:优先选择
fmt.Errorf加%w包装错误的写法,附加清晰的上下文信息方便排查。 - 逻辑简单、上下文清晰的场景:可以直接返回原始错误。
- 顶层入口逻辑:错误无需向上抛出时,再选择打日志后返回的写法。
内容的提问来源于stack exchange,提问作者wymli

