program tip

Go 오류 처리 기술

radiobox 2020. 8. 10. 07:54
반응형

Go 오류 처리 기술


방금 Go를 시작했습니다. 내 코드에 다음이 많이 포함되기 시작했습니다.

   if err != nil {
      //handle err
   }

아니면 이거

  if err := rows.Scan(&some_column); err != nil {
      //handle err
  }

Go에서 오류를 확인하고 처리하기위한 좋은 관용구 / 전략 / 모범 사례가 있습니까?

명확히하기 위해 편집 : 나는 Go 팀이 더 나은 것을 생각해 낼 것을 제안하거나 제안하지 않습니다. 내가 제대로하고 있는지 아니면 커뮤니티에서 제안한 몇 가지 기술을 놓쳤는 지 묻습니다. 모두 감사합니다.


귀하의 코드는 관용적이며 제 생각에는 사용 가능한 모범 사례입니다. 어떤 사람들은 확실히 동의하지 않을 것이지만 나는 이것이 Golang의 표준 라이브러리 전체에서 볼 수있는 스타일이라고 주장합니다 . 즉, Go 작성자는 이러한 방식으로 오류 처리를 작성합니다.


이 질문을받은 지 6 개월 후 Rob Pike는 Errors are Values 라는 제목의 블로그 게시물을 작성했습니다 .

거기에서 그는 OP가 제시하는 방식으로 프로그래밍 할 필요가 없다고 주장하고 표준 라이브러리에서 다른 패턴을 사용하는 여러 위치를 언급합니다.

물론 오류 값과 관련된 일반적인 진술은 그것이 nil인지 여부를 테스트하는 것입니다.하지만 오류 값으로 할 수있는 다른 일이 무수히 많으며, 이러한 다른 것들을 적용하면 프로그램을 더 좋게 만들 수 있으며 많은 상용구를 제거 할 수 있습니다. 모든 오류가 rote if 문으로 확인되면 발생합니다.

...

언어를 사용하여 오류 처리를 단순화하십시오.

그러나 기억하십시오. 무엇을하든 항상 오류를 확인하십시오!

좋은 읽기입니다.


나는 그들이 모두 관용적 코드라는 jnml의 대답에 동의하고 다음을 추가합니다.

첫 번째 예 :

if err != nil {
      //handle err
}

하나 이상의 반환 값을 다룰 때 더 관용적입니다. 예를 들면 :

val, err := someFunc()
if err != nil {
      //handle err
}
//do stuff with val

두 번째 예는 값만 다룰 때 좋은 속기 err입니다. 이는 함수가를 반환하는 경우에만 적용 error되거나 error. 예를 들어, 이것은 때때로 쓰여진 바이트 수 (때로는 불필요한 정보) 를 반환하는 ReaderWriter함수 와 함께 사용됩니다 .interror

if _, err := f.Read(file); err != nil {
      //handle err
}
//do stuff with f

두 번째 형식은 if 초기화 문 사용이라고 합니다 .

따라서 모범 사례와 관련하여 내가 아는 한 ( "errors" 패키지를 사용하여 필요할 때 새로운 오류를 생성 하는 것을 제외 하고) Go!의 오류에 대해 알아야 할 거의 모든 것을 다루었습니다.

편집 : 예외없이 정말 살 수 없다는 것을 알게된다면 defer,, panic&recover .


Go 함수 대기열을 통해 간소화 된 오류 처리 및 파이핑을위한 라이브러리를 만들었습니다.

여기에서 찾을 수 있습니다 : https://github.com/go-on/queue

간결하고 장황한 구문 변형이 있습니다. 다음은 짧은 구문의 예입니다.

import "github.com/go-on/queue/q"

func SaveUser(w http.ResponseWriter, rq *http.Request) {
    u := &User{}
    err := q.Q(                      
        ioutil.ReadAll, rq.Body,  // read json (returns json and error)
    )(
        // q.V pipes the json from the previous function call
        json.Unmarshal, q.V, u,   // unmarshal json from above  (returns error)
    )(
        u.Validate,               // validate the user (returns error)
    )(
        u.Save,                   // save the user (returns error)
    )(
        ok, w,                    // send the "ok" message (returns no error)
    ).Run()

    if err != nil {
       switch err {
         case *json.SyntaxError:
           ...
       }
    }
}

리플렉션을 사용하기 때문에 약간의 성능 오버 헤드가 있음을 유의하십시오.

또한 이것은 관용적 인 go 코드가 아니므로 자신의 프로젝트에서 사용하거나 팀이 사용에 동의하는 경우 사용하고 싶을 것입니다.


A "strategy" for handling errors in golang and in other languages is to continuously propogate errors up the call stack until you are high enough in the call stack to handle that error. If you tried handling that error too early, then you will likely end up repeating code. If you handle it too late, then you will break something in your code. Golang makes this process super easy as it makes it super clear whether you are handling an error at a given location or propagating it up.

If you are going to be ignoring the error, a simple _ will reveal this fact very clearly. If you are handling it, then exactly which case of the error you are handling is clear as you will check for it in the if statement.

Like people said above, an error is actually just a normal value. This treats it as such.


The Go gods have published a "draft design" for error handling in Go 2. It aims to change the errors idiom:

Overview and Design

They want feedback from users!

Feedback wiki

Briefly, it looks like:

func f() error {
   handle err { fmt.Println(err); return err }
   check mayFail()
   check canFail()
}

UPDATE: The draft design has received a lot of criticism, so I drafted Requirements to Consider for Go 2 Error Handling with a menu of possibilities for an eventual solution.


Most in industry, are following standard rules mentioned in golang documentation Error handling and Go. And it also helps doc generation for project.


Below is my take on reducing error handling on for Go, sample is for when getting HTTP URL parameters :

(Design pattern derived from https://blog.golang.org/errors-are-values)

type HTTPAdapter struct {
    Error *common.AppError
}

func (adapter *HTTPAdapter) ReadUUID(r *http.Request, param string, possibleError int) uuid.UUID {
    requestUUID := uuid.Parse(mux.Vars(r)[param])
    if requestUUID == nil { 
        adapter.Error = common.NewAppError(fmt.Errorf("parameter %v is not valid", param),
            possibleError, http.StatusBadRequest)
    }
    return requestUUID
}

calling it for multiple possible parameters would be as below:

    adapter := &httphelper.HTTPAdapter{}
    viewingID := adapter.ReadUUID(r, "viewingID", common.ErrorWhenReadingViewingID)
    messageID := adapter.ReadUUID(r, "messageID", common.ErrorWhenReadingMessadeID)
    if adapter.Error != nil {
        return nil, adapter.Error
    }

This is not a silver bullet, the downside is if you had multiple errors you are only able to get the last error.

But in this instance it is relatively repetitive and low risk, hence I can just get the last possible error.


You can clean up your error handling code for similar errors (since errors are values you have to be careful here) and write a function which you call with the error passed in to handle the error. You won't have to write "if err!=nil {}" every time then. Again, this will only result in cleaning up the code, but I don't think it is the idiomatic way of doing things.

Again, just because you can doesn't mean you should.


goerr allows to handle errors with functions

package main

import "github.com/goerr/goerr"
import "fmt"

func ok(err error) {
    if err != nil {
        goerr.Return(err)
        // returns the error from do_somethingN() to main()
        // sequence() is terminated
    }
}

func sequence() error {
    ok(do_something1())
    ok(do_something2())
    ok(do_something3())

    return nil /// 1,2,3 succeeded
}
func do_something1() error { return nil }
func do_something2() error { return fmt.Errorf("2") }
func do_something3() error {
    fmt.Println("DOING 3")
    return nil
}

func main() {
    err_do_something := goerr.OR1(sequence)

    // handle errors

    fmt.Println(err_do_something)
}

If you want precise control of errors, this may not be the solution, but for me, most of the time, any error is a show stopper.

So, I use functions instead.

func Err(err error) {
    if err!=nil {
        fmt.Println("Oops", err)
        os.Exit(1)
    }
}

fi, err := os.Open("mmm.txt")
Err(err)

참고URL : https://stackoverflow.com/questions/16963298/go-error-handling-techniques

반응형