Google erross의 Is와 As를 조사했습니다.

33311 단어 golangerrortech

erross의 Is, As는?

  • https://pkg.go.dev/errors
  • erross가 1.13에서 Is,As의 처리를 가져왔음
  • go의 오류를 판단하는 데 사용할 수 있는 처리
  • errors.Is란?

  • 공식
  • https://pkg.go.dev/errors#Is
  • 비교적 확실한 대상이면 변하지 않는다
  • Is가 자라면 거기서 비교
  • 언랩이 길면 다음 순환
  • 코드로 다음과 같이 표시한다
  • package main  
      
    import (  
       "errors"  
     "fmt")  
      
    type myError struct {  
       s string  
    }  
      
    func (obj *myError) Error() string {  
       return ""  
    }  
      
    type myErrorImplementedIs struct {  
       s string  
    }  
      
    func (obj *myErrorImplementedIs) Is(err error) bool {  
       return true  
    }  
      
    func (obj *myErrorImplementedIs) Error() string {  
       return ""  
    }  
      
    func main() {  
       // 比較可能で同じオブジェクトを返す場合は真  
     {  
          err := &myError{s:"hoge"}  
          fmt.Println(errors.Is(err, err))  
       }  
      
       // 比較可能でもオブジェクトが違えば偽  
     {  
          err := &myError{s:"hoge"}  
          target := errors.New("hoge")  
          fmt.Println(errors.Is(err, target))  
       }  
      
      // オブジェクトが違っていてもerrにIsが実装されていればそれを返す  
      {  
          err := &myErrorImplementedIs{s:"hoge"}  
          target := errors.New("hoge")  
          fmt.Println(errors.Is(err, target))  
       }  
      
      // Unwrapが実装されていれば返り値が比較可能かIsで判定する  
      // errのwrapのどれかに同一のオブジェクトか真を返すIsがあれば通る  
      {  
          target := &myError{s:"hoge"}  
          err := fmt.Errorf("%w",target)  
          fmt.Println(errors.Is(err, target))  
       }  
    }
    

    errors.Is의 내용

  • Is의 내용은 다음과 같다
  • 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  
    	  }  
         if err = Unwrap(err); err == nil {  
    	    return false  
    	 }
       }  
    }
    

    비교 가능한 판단

  • reflect를 사용하여 비교 비교 대상을 비교할 수 있는지 판단
  • 비교할 수 없는 상황에서runtime error가 발생하므로 체크합니다
    + 다음 URL 상세 정보
    + https://golang.org/ref/spec#Comparison_operators
    + https://pod.hatenablog.com/entry/2016/07/30/204357
    + https://golang.org/ref/spec#Assignability
    + https://budougumi0617.github.io/2019/07/07/prevent-runtime-error-by-pointer/
  • isComparable := reflectlite.TypeOf(target).Comparable() 
    
  • err와 target의 상황을 비교할 수 있음
  • 최초 판정에서 진
  • if isComparable && err == target {  
    	return true  
    }  
    

    err Is 메소드 보유 시

  • err Is 방법이 있을 때
  • err의 Is에 target 건네기 비교
  • if x, ok := err.(interface{ Is(error) bool }); ok && x.Is(target) {  
    	return true  
    }  
    

    err Unwrap, nil을 반납하면 끝이에요.


    if err = Unwrap(err); err == nil {
    	return false
    }
    
  • 이 Unwarp의 내용도 상기 Is 판정과 동일한 처리
  • Unwarp 메서드를 저장하면 반환
  • func Unwrap(err error) error {  
       u, ok := err.(interface { Unwrap() error })  
       if !ok {  
          return nil  
       }  
       return u.Unwrap()  
    }
    

    error.As란 무엇입니까?

  • errors.As는 첫 번째 파라미터에err를 전송하고 두 번째 파라미터에 존재할 수 있는 오류 형식을 전달합니다
  • 만약 제2대입을 할 수 있다면 대입하고 진짜로 되돌아온다
  • 대입이 불가능한 경우
  • As 구현 여부 조사
  • 실현되면 As로 판정
  • Unwrap 설치 여부를 판단하면err
  • 대입
  • Loop 계속
  • package main
    
    import (
    	"errors"
    	"fmt"
    )
    
    type myError struct {}
    
    func (obj *myError) Error() string {
    	return "myError"
    }
    
    type myErrorAs struct {}
    
    func (obj *myErrorAs) Error() string {
    	return "myErrorAs"
    }
    
    func (obj *myErrorAs) As(_ interface{}) bool {
    	return true
    }
    
    
    func main() {
    	// targetがinterfaceだと全てにmatchするので真
    	{
    		err := &myError{}
    		var target interface{}
    		fmt.Println(errors.As(err,&target))
    	}
    
    	// targetが同一の型なら代入可能なので真
    	{
    		err := &myError{}
    		var target *myError
    		fmt.Println(errors.As(err,&target))
    	}
    
    	// errがAsを実装されている場合はそこで判定
    	// この場合err.Asが常に真
    	{
    		err := &myErrorAs{}
    		var target *myError
    		fmt.Println(errors.As(err,&target))
    	}
    }
    

    errors.As의 내용

  • 한마디로 나쁜 데이터를 내면 바로panic
  • 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")  
       }  
       targetType := typ.Elem()  
       if targetType.Kind() != reflectlite.Interface && !targetType.Implements(errorType) {  
          panic("errors: *target must be interface or implement error")  
       }  
       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  
    }
    
  • target이nil이면panic
  • if target == nil {  
       panic("errors: target cannot be nil")  
    }
    
  • target의 타입이 Ptr가 아니거나 Ptr에서nil인 경우panic
  • val := reflectlite.ValueOf(target)  
    typ := val.Type()  
    if typ.Kind() != reflectlite.Ptr || val.IsNil() {  
       panic("errors: target must be a non-nil pointer")  
    }
    
  • 데이터 형식이interface에서 오류 형식이 아니라면panic
  • targetType := typ.Elem()  
    if targetType.Kind() != reflectlite.Interface && !targetType.Implements(errorType) {  
       panic("errors: *target must be interface or implement error")  
    }
    

    대입 가능 여부를 판단하다

  • 다음은 처리의 주체
  • err의 AssignableTo 확인
  • https://qiita.com/tenntenn/items/dfc112ae7bb8c9703ab9
  • types.AssignableTo 함수: 첫 번째 파라미터 유형의 값이 두 번째 파라미터 유형의 변수를 대입할 수 있는지 검사
  • 대입이 가능하면 target을 대입하고 되돌아오기
  • 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)  
    }
    

    총결산

  • Is의 여부同一에 따라 As가 이 유형인지 아닌지代入可能
  • err에서 부적합한 조건을 실현하면 Is와 As
  • 를 실행합니다
  • 만약 여전히 안 된다면 Unwrap을 실행하여 다음err
  • 를 검사합니다
  • 편하니까 사용하세요
  • 좋은 웹페이지 즐겨찾기