Golang study notes-error handling

Golang error handling

1.1 Error support for Golang official library

(1) Golang errors are relatively lightweight, and Error errors only need to implement the error interface under the buildin package.

  type error interface {
    Error() string
  }

(2) Goland's default support is implemented as the implementation under the errors package

  // errors.go文件
  
  package errors

  // New returns an error that formats as the given text.
  // Each call to New returns a distinct error value even if the text is identical.
  func New(text string) error {
    return &errorString{text}
  }

  // errorString is a trivial implementation of error.
  type errorString struct {
    s string
  }

  func (e *errorString) Error() string {
    return e.s
  }

(3) Wrap support is supported after 1.3, mainly supported by Unwrap, Is and As methods. Also use %w to implement the wrap function, but does not contain stack information

// wrap.go

  func Unwrap(err error) error {
    u, ok := err.(interface {
      Unwrap() error
    })
    if !ok {
      return nil
    }
    return u.Unwrap()
  }

  func Is(err, target error) bool {
    if target == nil {
      return err == target
    }

    isComparable := reflectlite.TypeOf(target).Comparable()
    for {
      if isComparable && err == target {
        return true
      }
      if x, ok := err.(interface{ Is(error) bool }); ok && x.Is(target) {
        return true
      }
      // TODO: consider supporting target.Is(err). This would allow
      // user-definable predicates, but also may allow for coping with sloppy
      // APIs, thereby making it easier to get away with them.
      if err = Unwrap(err); err == nil {
        return false
      }
    }
  }

  func As(err error, target interface{}) bool {
    if target == nil {
      panic("errors: target cannot be nil")
    }
    val := reflectlite.ValueOf(target)
    typ := val.Type()
    if typ.Kind() != reflectlite.Ptr || val.IsNil() {
      panic("errors: target must be a non-nil pointer")
    }
    if e := typ.Elem(); e.Kind() != reflectlite.Interface && !e.Implements(errorType) {
      panic("errors: *target must be interface or implement error")
    }
    targetType := typ.Elem()
    for err != nil {
      if reflectlite.TypeOf(err).AssignableTo(targetType) {
        val.Elem().Set(reflectlite.ValueOf(err))
        return true
      }
      if x, ok := err.(interface{ As(interface{}) bool }); ok && x.As(target) {
        return true
      }
      err = Unwrap(err)
    }
    return false
  }

  var errorType = reflectlite.TypeOf((*error)(nil)).Elem()

1.2 Extension pkg/errors implemented by golang official library

Extended errors support: https://github.com/pkg/errors

pkg/errors provides a package for the official library error. In addition to providing error information, it can also track the error stack information

1.3 The posture of golang's error type handling

(1). Sentinel Error, a predefined specific error, refers to a defined error created by the errors.New() method, for example: var Canceled = errors.New("context canceled")

(2). Error type, self-defined error type, means to define an error type by yourself and implement the error interface

  type MyError struct {
    Msg string
    File string;
    Line int
  }
  
  func (e *MyError) Error() string {
    return fmt.Sprintf("%s:%d:%s",e.File,e.Line,e.Msg)
  }
  
  func test() error {
    return &MyError{"Something happened","server.go",42}
  }

(3). Opaque errors, transparent errors, refer to only providing external information about whether an error has occurred, without providing specific error content information, as follows:

    type temporary interface {
      Tempporary() bool
    }

    func IsTemporary(err error) bool {
      te,ok := err.(temporary)
      return ok && te.Temporary()
    }

Provide IsTemporary() method externally to detect whether it is an error, without providing specific information externally

 

1.4 The practice of golang error handling

1. In business system applications, if a function written by yourself returns an error, use errors.New or errors.Errorf to return the error (stored stack information) (the pkg/errors package used here)

2. In the business system application, if it is an error returned by other packages of the business system or the project, the error will be returned directly (without packaging), for example, the service layer calls dao

3. In the business system application, if the third-party library, standard library or basic library of your company is called, use Wrap or Wrapf to wrap the root cause (the lowest error message)

4. Print the log at the top of the program or the top of the working goroutine, and use %+v to record the stack information. If the log has been recorded, there is no need to throw the error to the upper level.

5. At the top level, use errors.Cause to get the root error (root cause), and then judge the sentinel error

6. In business system applications, use error code (unified error code) to return to the outside world

1.5 Simple pseudo code implementation


    var (
    	ErrDataNotFound = errors.New("record not found")
    )

    type User struct {
        Id uint64 `json:"Id"`
        Name string `json:"name"`
        Age  int32  `json:"age"`
    }

    type Dao interface {
        Get(id uint64) interface{}
        List() interface{}
        Create() 
        Update()
        Delete(id uint64) 
    }

    type UserDao struct {}
    
    // Dao层获取到底层错误,使用errors的Wrap进行包装
    fuc(user *UserDao) Get(id uint64) (*User, error) {
        user := User{}
        err := db.Where("id = ?",id).Find(user).Error
        
        if errors.Is(err,sql.ErrNoRows){
            retrun errors.Wrap(err,fmt.Sprintf("find user null,user id: %v",id))
        }
        return &user,nil
    }

    // 业务层获取到错误直接往上层抛
    type UserService struct {}
    
    func (s *Service) FindUserByID(userID int) (*model.User, error) {
        return dao.FindUserByID(userID)
    }

Guess you like

Origin blog.csdn.net/keenw/article/details/112292209