Thực hành tốt nhất - Xử lý lỗi

Đây là bài viết đầu tiên trong một loạt các bài học mà tôi đã học được trong vài năm tôi đã làm việc với Go trong sản xuất. Chúng tôi đang điều hành một số lượng lớn các dịch vụ Go trong sản xuất tại Saltside Technologies (psst, I triệtm đang tuyển dụng cho nhiều vị trí ở Bangalore cho Saltside) và tôi cũng đang điều hành doanh nghiệp của riêng mình trong đó Go là một phần không thể thiếu.

Chúng tôi sẽ bao gồm một loạt các đối tượng, lớn và nhỏ.

Chủ đề đầu tiên tôi muốn trình bày trong loạt bài này là xử lý lỗi. Nó thường gây nhầm lẫn và phiền toái cho các nhà phát triển Go mới.

Một số nền tảng - Giao diện lỗi

Chỉ để chúng tôi trên cùng một trang. Như bạn có thể biết một lỗi trong Go chỉ đơn giản là bất cứ điều gì thực hiện giao diện lỗi. Đây là định nghĩa giao diện:

loại giao diện lỗi {
    Chuỗi lỗi ()
}

Vì vậy, bất cứ điều gì thực hiện phương thức chuỗi Error () đều có thể được sử dụng như một lỗi.

Kiểm tra lỗi

Sử dụng cấu trúc lỗi và kiểm tra loại

Khi tôi bắt đầu viết Go, tôi thường thực hiện so sánh chuỗi các thông báo lỗi để xem loại lỗi là gì (vâng, lúng túng khi nghĩ đến nhưng đôi khi bạn cần nhìn lại để đi tiếp).

Một cách tiếp cận tốt hơn là sử dụng các loại lỗi. Vì vậy, bạn có thể (tất nhiên) tạo các cấu trúc thực hiện giao diện lỗi và sau đó thực hiện so sánh kiểu trong câu lệnh chuyển đổi.

Ở đây, một ví dụ thực hiện lỗi.

loại ErrZeroDivision struct {
    chuỗi tin nhắn
}
func NewErrZeroDivision (chuỗi tin nhắn) * ErrZeroDivision {
    trả lại & ErrZeroDivision {
        tin nhắn: tin nhắn,
    }
}
func (e * ErrZeroDivision) Lỗi () chuỗi {
    trở lại
}

Bây giờ lỗi này có thể được sử dụng như thế này.

func chính () {
    kết quả, err: = chia (1.0, 0.0)
    nếu lỗi! = nil {
        chuyển đổi lỗi. (loại) {
        trường hợp * ErrZeroDivision:
            fmt.Println (err.Error ())
        mặc định:
            fmt.Println ("Chuyện gì đã xảy ra?")
        }
    }
    fmt.Println (kết quả)
}
chia func (a, b float64) (float64, lỗi) {
    nếu b == 0,0 {
        trả về 0,0, NewErrZeroDivision ("Không thể chia cho số 0")
    }
    trả lại a / b, không
}

Ở đây, liên kết Go Play cho ví dụ đầy đủ. Lưu ý mẫu lỗi. (Loại), điều này cho phép kiểm tra các loại lỗi khác nhau thay vì một thứ khác (như so sánh chuỗi hoặc một cái gì đó tương tự).

Sử dụng gói lỗi và so sánh trực tiếp

Cách tiếp cận trên có thể được xử lý thay thế bằng cách sử dụng gói lỗi. Cách tiếp cận này được khuyến nghị để kiểm tra lỗi trong gói nơi bạn cần biểu diễn lỗi nhanh.

var errNotFound = lỗi.New ("Không tìm thấy mục")
func chính () {
    err: = getItem (123) // Điều này sẽ ném errNotFound
    nếu lỗi! = nil {
        chuyển đổi lỗi {
        trường hợp errNotFound:
            log.Println ("Không tìm thấy mục yêu cầu")
        mặc định:
            log.Println ("Xảy ra lỗi không xác định")
        }
    }
}

Cách tiếp cận này kém hơn khi bạn cần các đối tượng lỗi phức tạp hơn với ví dụ: mã lỗi, vv Trong trường hợp đó, bạn nên tạo kiểu riêng của mình thực hiện giao diện lỗi.

Xử lý lỗi ngay lập tức

Đôi khi tôi bắt gặp mã như bên dưới (nhưng thường có nhiều lông tơ hơn ..):

func example1 () lỗi {
    err: = call1 ()
    trở lại lỗi
}

Vấn đề ở đây là lỗi không được xử lý ngay lập tức. Đây là một cách tiếp cận dễ vỡ vì ai đó có thể chèn mã giữa err: = call1 () và return return, điều này sẽ phá vỡ ý định, vì điều đó có thể che mờ lỗi đầu tiên. Hai cách tiếp cận khác nhau:

// Thu gọn trả lại và lỗi.
func example2 () lỗi {
    trả về cuộc gọi1 ()
}
// Thực hiện xử lý lỗi rõ ràng ngay sau cuộc gọi.
func example3 () lỗi {
    err: = call1 ()
    nếu lỗi! = nil {
        trở lại lỗi
    }
    trở về
}

Cả hai cách tiếp cận trên đều tốt với tôi. Họ đạt được điều tương tự, đó là; nếu ai đó cần thêm một cái gì đó sau cuộc gọi1 (), họ cần xử lý lỗi.

Đó là tất cả cho ngày hôm nay

Hãy theo dõi các bài viết tiếp theo về Thực hành tốt nhất Go. Hãy mạnh mẽ :).

func chính () {
    err: = readArticle ("Thực hành tốt nhất - Xử lý lỗi")
    nếu lỗi! = nil {
        ping ("@ sebdah")
    }
}