Golang如何优雅处理错误(3)

95 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第9天,点击查看活动详情

本节继续扩展下关于error的一些编码规则吧,只是遇到感觉好的一些点,官方是没有统一的规范约束滴。

  • 处理所有含有err的返回值,不使用_忽略错误返回值
反面例子 
buf, _ := json.Marshal(e)
正面例子 
// Error decodes response error for a string of golang dubbo.
buf, err := json.Marshal(e)
  if err != nil {
     msg, retryErr := json.Marshal(err.Error())
     if retryErr != nil {
        msg = []byte("jsonrpc2.Error: json.Marshal failed")
     }
     return fmt.Sprintf(`{"code":%d,"message":%s}`, -32001, string(msg))
  }

  • 多次重复使用的错误,可以分组统一定义
 // error type group A
 (

         ErrA = errors.New("A")

         ErrAA= errors.New("AA")

         ErrAAA = errors.New("AAA")

     ...   

 )

 // error type group B
 (

         ErrB = errors.New("B")

         ErrBB = errors.New("BB")

     ...

 )

  • 失败的原因只有一个时或者当函数的error返回值始终是nil时,不使用error
**反例:**

    func (self *AgentContext) CheckHostType(hostType string) error {

            switch hostType {

            case "virtual_machine":

                return nil

            case "bare_metal":

                return nil

            }

            return ErrInvalidHostType
    }
    
**正例:**

func (self *AgentContext) IsValidHostType(hostType string) bool {

        return hostType == "virtual_machine" || hostType == "bare_metal"

}

  • error应放在返回值类型列表的最后

func NewConfig() (*Config, error) {
  data, err := bootstrapConfigFromEnvVariable()
  if err != nil {
     return nil, fmt.Errorf("xds: Failed to read bootstrap config: %v", err)
  }
  dubbogoLogger.Debugf("Bootstrap content: %s", data)
  return NewConfigFromContents(data)
}
  • 巧用defer错误后清理资源
**反面例子**
   func New() (_ *Controller, retErr error) {
   ret := &Controller{
   
      streamCh:        make(chan grpc.ClientStream, 1),
      watchMap:        make(map[resource.ResourceType]map[string]bool),
      versionMap:      make(map[resource.ResourceType]string),
      nonceMap:        make(map[resource.ResourceType]string),
      lrsClients:      make(map[string]*lrsClient),
   }
   
    builder1, err := builder1(resource1)
   if err != nil {
      return nil, fmt.Errorf("failed to build resource1 {%s}: %v", "info1", err)
   }

   builder2 := builder2(resource2)
   if builder2 == nil {
      closeResource1()
      return nil, fmt.Errorf("failed to build resource2 {%s}: %v", "info2", err)
   }
   
   builder3, err := builder3(resource3)
   if err != nil {
      closeResource1()
      closeResource2()
      return nil, err
   }
   
   go ret.run(ctx)
   
   return ret, nil
}
**正面例子:**
// New creates a new controller.
func New() (_ *Controller, retErr error) {
   ret := &Controller{
   
      streamCh:        make(chan grpc.ClientStream, 1),
      watchMap:        make(map[resource.ResourceType]map[string]bool),
      versionMap:      make(map[resource.ResourceType]string),
      nonceMap:        make(map[resource.ResourceType]string),
      lrsClients:      make(map[string]*lrsClient),
   }

   defer func() {
      if retErr != nil {
      // close all resources and ret
         ret.Close()
         closeResource1()
         closeResource2()
         closeResource3()
      }
   }()

    builder1, err := builder1(resource1)
   if err != nil {
      return nil, fmt.Errorf("failed to build resource1 {%s}: %v", "info1", err)
   }

   builder2 := builder2(resource2)
   if builder2 == nil {
      return nil, fmt.Errorf("failed to build resource2 {%s}: %v", "info2", err)
   }
   
   builder3, err := builder3(resource3)
   if err != nil {
      return nil, err
   }
   
   go ret.run(ctx)
   
   return ret, nil
}

  • 因为消息可能发生嵌套wrap场景,为了更好看错误消息应以小写字母开头,不应以.结尾
反面例子  
ErrReadFailed := errors.New("Could not find file") 
正面例子  
ErrReadFailed := errors.New("could not find file")