Bắt đầu kỹ thuật xử lý lỗi [đã đóng]


108

Tôi chỉ mới bắt đầu với cờ vây. Mã của tôi bắt đầu có rất nhiều điều này:

   if err != nil {
      //handle err
   }

hoặc cái này

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

Có một số thành ngữ / chiến lược / phương pháp hay nhất để kiểm tra và xử lý lỗi trong cờ vây không?

CHỈNH SỬA để làm rõ: Tôi không khoe khoang hay gợi ý rằng đội cờ vây nên nghĩ ra thứ gì đó tốt hơn. Tôi đang hỏi liệu tôi có đang làm đúng hay không hay tôi đã bỏ lỡ một số kỹ thuật mà cộng đồng đã đưa ra. Cảm ơn tất cả.


4
Không, không hẳn vậy. Đó là một chủ đề thường được thảo luận và là một chủ đề hợp lý. Có nhiều đề xuất tiến hóa quá. Câu trả lời của nhóm dường như là nó không phải là một vấn đề trong một đoạn mã được viết tốt.
Denys Séguret


Lưu ý rằng câu hỏi liên quan này không thực sự giống với câu hỏi này. Câu trả lời quá cụ thể.
Denys Séguret

Cũng có lý do cho sự khó chịu này: nó làm cho việc viết một chương trình trở nên khó khăn hơn nhưng nó cũng khiến việc tạo ra lỗi khó hơn bằng cách đơn giản là ném lại lỗi.
Denys Séguret

Bạn có thể thấy Andrew Gerrand và Brad Fitzpatrick viết phần khởi đầu của một ứng dụng khách HTTP / 2 trong Go theo thời trang ít nhiều tương tự youtube.com/watch?v=yG-UaBJXZ80
Supreet Sethi

Câu trả lời:


61

Mã của bạn là thành ngữ và theo ý kiến ​​của tôi, đó là phương pháp hay nhất hiện có. Chắc chắn một số sẽ không đồng ý, nhưng tôi cho rằng đây là phong cách được thấy ở khắp các thư viện tiêu chuẩn ở Golang . Nói cách khác, các tác giả Go viết xử lý lỗi theo cách này.


12
"Tác giả Go viết xử lý lỗi theo cách này." Nghe có vẻ tốt với tôi.
gmoore

"Một số chắc chắn sẽ không đồng ý" : Tôi không chắc ai đó sẽ nói rằng đó không phải là phương pháp hay nhất hiện nay. Một số hỏi đường cú pháp hoặc các thay đổi khác nhưng hôm nay tôi không nghĩ rằng bất kỳ lập trình viên nghiêm túc nào sẽ kiểm tra lỗi theo cách khác.
Denys Séguret

@dystroy: OK, một số nói " it sux ", những người khác gọi đó là "lỗi được xử lý trong các giá trị trả về. Kiểu của 70". , v.v. ;-)
zzzz

2
@jnml Xử lý lỗi theo cách này là vấn đề của thiết kế ngôn ngữ, đây là một chủ đề gây nhiều tranh cãi. May mắn thay, có hàng tá ngôn ngữ để bạn lựa chọn.
fuz

4
Điều giết chết tôi là cách sử dụng cùng một mẫu cho mọi lệnh gọi hàm. Điều này làm cho mã khá ồn ào ở một số nơi và chỉ đang kêu gào về cú pháp để đơn giản hóa mã mà không làm mất bất kỳ thông tin nào, về cơ bản là định nghĩa của tính ngắn gọn (mà tôi tin là thuộc tính cao hơn tính dài dòng, nhưng đó được cho là một điều gây tranh cãi điểm). Nguyên tắc là âm thanh, nhưng cú pháp để lại rất nhiều để được IMHO mong muốn. Tuy nhiên, việc phàn nàn bị cấm, vì vậy tôi sẽ chỉ uống kool-aid của tôi bây giờ ;-)
Thomas

30

Sáu tháng sau khi câu hỏi này được hỏi, Rob Pike đã viết một bài blog có tiêu đề Lỗi là Giá trị .

Trong đó, ông lập luận rằng bạn không cần phải lập trình theo cách mà OP đã trình bày, và đề cập đến một số nơi trong thư viện tiêu chuẩn, nơi họ sử dụng một mẫu khác.

Tất nhiên, một câu lệnh phổ biến liên quan đến giá trị lỗi là để kiểm tra xem nó có phải là giá trị không, nhưng có vô số điều khác mà người ta có thể làm với giá trị lỗi và việc áp dụng một số trong những thứ đó có thể làm cho chương trình của bạn tốt hơn, loại bỏ phần lớn bảng soạn sẵn điều đó phát sinh nếu mọi lỗi được kiểm tra bằng câu lệnh if thuộc lòng.

...

Sử dụng ngôn ngữ để đơn giản hóa việc xử lý lỗi của bạn.

Nhưng hãy nhớ rằng: Dù bạn làm gì, hãy luôn kiểm tra lỗi của bạn!

Đó là một bài đọc hay.


Cảm ơn! Sẽ kiểm tra điều đó.
gmoore

Bài viết thật tuyệt vời, về cơ bản nó giới thiệu một đối tượng có thể ở trạng thái không thành công, và nếu có nó sẽ chỉ bỏ qua mọi thứ bạn làm với nó và ở trạng thái không thành công. Đối với tôi, âm thanh gần như đơn nguyên.
Waterlink

@Waterlink Tuyên bố của bạn là vô nghĩa. Mọi thứ có trạng thái gần như là đơn nguyên, nếu bạn liếc nhìn nó một chút. Tôi nghĩ so sánh nó với en.wikipedia.org/wiki/Null_Object_pattern thì hữu ích hơn.
user7610

@ user7610, Cảm ơn bạn đã phản hồi. Tôi chỉ có thể đồng ý.
Waterlink

2
Pike: "Nhưng hãy nhớ rằng: Dù bạn làm gì, hãy luôn kiểm tra lỗi của mình!" - đây là những năm 80. Lỗi có thể xảy ra ở bất cứ đâu, hãy ngừng tạo gánh nặng cho các lập trình viên và áp dụng các ngoại lệ vì lợi ích của Pete.
Slawomir

22

Tôi sẽ đồng ý với câu trả lời của jnml rằng cả hai đều là mã thành ngữ và thêm những điều sau:

Ví dụ đầu tiên của bạn:

if err != nil {
      //handle err
}

thành ngữ hơn khi xử lý nhiều hơn một giá trị trả về. ví dụ:

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

Ví dụ thứ hai của bạn là cách viết tắt hay khi chỉ xử lý errgiá trị. Điều này áp dụng nếu hàm chỉ trả về một errorhoặc nếu bạn cố tình bỏ qua các giá trị được trả về khác với error. Ví dụ, điều này đôi khi được sử dụng với các hàm ReaderWritertrả về một intsố byte được viết (đôi khi là thông tin không cần thiết) và error:

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

Dạng thứ hai được coi là sử dụng câu lệnh khởi tạo if .

Vì vậy, liên quan đến các phương pháp hay nhất, theo như tôi biết (ngoại trừ việc sử dụng gói "lỗi" để tạo lỗi mới khi bạn cần), bạn đã đề cập đến khá nhiều thứ bạn cần biết về lỗi abut trong Go!

EDIT: Nếu bạn tìm thấy bạn thực sự không thể sống mà không có ngoại lệ, bạn có thể bắt chước họ defer, panic&recover .


4

Tôi đã tạo một thư viện để xử lý lỗi được sắp xếp hợp lý và chuyển qua một hàng đợi các hàm Go.

Bạn có thể tìm thấy nó ở đây: https://github.com/go-on/queue

Nó có một biến thể cú pháp nhỏ gọn và dài dòng. Đây là một ví dụ cho cú pháp ngắn gọn:

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:
           ...
       }
    }
}

Xin lưu ý rằng có một chút chi phí hiệu suất, vì nó sử dụng phản xạ.

Ngoài ra, đây không phải là mã cờ vây thành ngữ, vì vậy bạn sẽ muốn sử dụng nó trong các dự án của riêng mình hoặc nếu nhóm của bạn đồng ý sử dụng nó.


3
Chỉ vì bạn có thể làm được điều này, không có nghĩa đó là một ý kiến ​​hay. Điều này trông giống như mô hình Chuỗi trách nhiệm , ngoại trừ có lẽ khó đọc hơn (ý kiến). Tôi đề nghị nó không phải là "cờ vây thành ngữ". Thật thú vị.
Steven Soroka

2

Một "chiến lược" để xử lý lỗi trong golang và các ngôn ngữ khác là liên tục đề xuất lỗi lên ngăn xếp cuộc gọi cho đến khi bạn đủ cao trong ngăn xếp cuộc gọi để xử lý lỗi đó. Nếu bạn đã cố gắng xử lý lỗi đó quá sớm, thì rất có thể bạn sẽ phải lặp lại mã. Nếu bạn xử lý quá muộn, thì bạn sẽ phá vỡ một thứ gì đó trong mã của mình. Golang làm cho quá trình này trở nên siêu dễ dàng vì nó giúp bạn siêu rõ ràng cho dù bạn đang xử lý một lỗi tại một vị trí nhất định hay đang lan truyền nó.

Nếu bạn định bỏ qua lỗi, một dấu _ đơn giản sẽ tiết lộ sự thật này rất rõ ràng. Nếu bạn đang xử lý nó, thì chính xác trường hợp lỗi mà bạn đang xử lý sẽ rõ ràng vì bạn sẽ kiểm tra nó trong câu lệnh if.

Giống như mọi người đã nói ở trên, một sai số thực sự chỉ là một giá trị bình thường. Điều này đối xử với nó như vậy.


2

Các vị thần cờ vây đã xuất bản một "bản thiết kế nháp" để xử lý lỗi trong cờ vây 2. Nó nhằm mục đích thay đổi thành ngữ lỗi:

Tổng quanThiết kế

Họ muốn phản hồi từ người dùng!

Wiki phản hồi

Tóm lại, nó trông giống như:

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

CẬP NHẬT: Thiết kế dự thảo đã nhận được rất nhiều lời chỉ trích, vì vậy tôi đã soạn thảo Yêu cầu cần xem xét đối với Xử lý lỗi Go 2 với một menu các khả năng cho giải pháp cuối cùng.


1

Hầu hết trong ngành đều tuân theo các quy tắc tiêu chuẩn được đề cập trong tài liệu golang Xử lý lỗi và Bắt đầu . Và nó cũng giúp tạo tài liệu cho dự án.


Đây thực chất là một câu trả lời chỉ liên kết. Tôi khuyên bạn nên thêm một số nội dung vào câu trả lời để nếu liên kết trở nên không hợp lệ, câu trả lời của bạn sẽ vẫn được sử dụng.
Neo

cảm ơn bạn đã bình luận có giá trị.
pschilakanti 13/02/17

0

Dưới đây là hướng dẫn của tôi về việc giảm xử lý lỗi đối với Go, ví dụ là khi nhận các tham số URL HTTP:

(Mẫu thiết kế bắt nguồn từ 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
}

gọi nó cho nhiều tham số có thể sẽ như sau:

    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
    }

Đây không phải là một viên đạn bạc, nhược điểm là nếu bạn mắc nhiều lỗi, bạn chỉ có thể nhận được lỗi cuối cùng.

Nhưng trong trường hợp này, nó tương đối lặp lại và rủi ro thấp, do đó tôi chỉ có thể nhận được lỗi cuối cùng có thể xảy ra.


-1

Bạn có thể xóa mã xử lý lỗi của mình cho các lỗi tương tự (vì lỗi là giá trị mà bạn phải cẩn thận ở đây) và viết một hàm mà bạn gọi với lỗi được chuyển vào để xử lý lỗi. Bạn sẽ không phải viết "if err! = Nil {}" mọi lúc. Một lần nữa, điều này sẽ chỉ dẫn đến việc làm sạch mã, nhưng tôi không nghĩ đó là cách làm việc ngu ngốc.

Một lần nữa, chỉ vì bạn có thể không có nghĩa là bạn nên làm .


-1

goerr cho phép xử lý lỗi với các hàm

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)
}

Chà. Phức tạp / ẩn logic xử lý lỗi như thế này không phải là một ý kiến ​​hay của IMO. Mã kết quả (cần nguồn xử lý trước bởi goerr) khó đọc / lý giải hơn mã Go thành ngữ.
Dave C

-4

Nếu bạn muốn kiểm soát chính xác các lỗi, đây có thể không phải là giải pháp, nhưng đối với tôi, hầu hết thời gian, bất kỳ lỗi nào đều là một nút chặn hiển thị.

Vì vậy, tôi sử dụng các hàm để thay thế.

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

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

Những thông báo như vậy sẽ được chuyển đến stderrthay vì stdout, vì vậy chỉ cần sử dụng log.Fatal(err)hoặc log.Fatalln("some message:", err). Vì hầu như không có gì khác hơn là mainnên đưa ra quyết định như vậy để kết thúc toàn bộ chương trình (tức là trả về lỗi từ các hàm / phương thức, không hủy bỏ) trong trường hợp hiếm hoi, đây là điều bạn muốn làm, nó rõ ràng hơn và tốt hơn nên làm điều đó một cách rõ ràng (tức là if err := someFunc(); err != nil { log.Fatal(err) }) chứ không phải thông qua một chức năng "helper" không rõ ràng về những gì nó đang làm (tên "Err" không tốt, nó không cho thấy nó có thể chấm dứt chương trình).
Dave C

Học được điều mới! Cảm ơn bạn @DaveC
Gon
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.