Pelajaran modul (2/5)
Penanganan kesalahan idiomatis
Penanganan kesalahan di Go bersifat eksplisit: tidak ada pengecualian, tidak ada coba/tangkap. Setiap fungsi yang bisa gagal mengembalikan error sebagai nilai terakhirnya, dan pemanggil yang memutuskan. Bab ini mengumpulkan pola idiomatik untuk menghasilkan, menyebarkan, dan memeriksa kesalahan dengan cara yang kuat.
Aturan emas
data, err := os.ReadFile(path)
if err != nil {
return fmt.Errorf("config %s: %w", path, err)
}Empat prinsip:
erroradalah pengembalian terakhir.- Segera periksa dengan
if err != nil. - Tambahkan konteks sebelum melakukan propagasi (dengan
fmt.Errorf+%w). - Jangan pernah mengabaikan kesalahan: tangani, catat, atau kembalikan.
_ = file.Close() // EXPLICITLY ignored (you know what you're doing)Kesalahan penjaga
kesalahan penjaga adalah variabel global yang diekspor var ErrXxx = errors.New("...") yang dapat dibandingkan dengan pemanggil:
package store
var ErrNotFound = errors.New("not found")
func Get(key string) (string, error) {
if !exists(key) {
return "", ErrNotFound
}
// ...
}Penelepon menggunakan errors.Is (BUKAN ==), yang menjalankan rantai pembungkus:
v, err := store.Get(k)
if errors.Is(err, store.ErrNotFound) {
// handle 404
}Contoh standar: io.EOF, sql.ErrNoRows, os.ErrNotExist.
Kesalahan pengetikan khusus
Untuk membawa data terstruktur (kode HTTP, bidang tidak valid, dll.), tentukan jenisnya:
type ValidationError struct {
Field string
Msg string
}
func (e *ValidationError) Error() string {
return fmt.Sprintf("%s: %s", e.Field, e.Msg)
}Penelepon menggunakan errors.As untuk melakukan downcast dengan aman di sepanjang rantai:
var ve *ValidationError
if errors.As(err, &ve) {
fmt.Println("invalid field:", ve.Field)
}errors.As menelusuri rantai Unwrap() hingga menemukan kesalahan yang dapat ditetapkan ke penunjuk yang diteruskan.
Membungkus dengan %w
fmt.Errorf dengan kata kerja %w menghasilkan kesalahan yang membungkus kata kerja asli, mempertahankannya untuk errors.Is/errors.As:
if err := db.Query(); err != nil {
return fmt.Errorf("load users: %w", err)
}Tanpa %w (menggunakan %v atau %s) Anda mendapatkan string yang lebih kaya tetapi kehilangan rantainya: pemanggil tidak dapat lagi mengenali kesalahan tertentu.
Aturan untuk %w:
- Hanya satu per
fmt.Errorfpanggilan (multi-bungkus memerlukanerrors.Join). - Argumennya HARUS berupa
error, bukan string. - Tambahkan konteks, jangan duplikat pesan aslinya.
// bad: duplicates
return fmt.Errorf("error: %w", err) // "error: file not found"
// good: useful context
return fmt.Errorf("load config %s: %w", path, err)errors.Join untuk beberapa kesalahan
Sejak Go 1.20, errors.Join(errs...) menggabungkan beberapa kesalahan menjadi satu sehingga semuanya dapat terdeteksi oleh errors.Is/As:
var errs []error
for _, item := range items {
if err := process(item); err != nil {
errs = append(errs, fmt.Errorf("item %s: %w", item.ID, err))
}
}
if len(errs) > 0 {
return errors.Join(errs...)
}Kapan menggunakan panic
Tidak pernah untuk kesalahan yang diharapkan. Hanya dalam tiga kasus:
- Bug yang tidak dapat dipulihkan: invarian dilanggar di tempat yang "tidak dapat terjadi" (
panic("unreachable")). - Inisialisasi gagal di
init()/main()dari program mandiri (konfigurasi penting tidak ada → cepat gagal). - Library API yang mendokumentasikannya secara eksplisit: misalnya,
regexp.MustCompile(panik jika polanya tidak valid — dapat diterima karena polanya adalah konstanta waktu pembangunan).
Yang lainnya adalah error. Jangan gunakan panic sebagai pintasan untuk menyebarkan: ini merusak komposisi, melompati defer Close(), dan membuat penelepon kewalahan.
Latihan
Selesaikan kesalahan os.ReadFile dengan fmt.Errorf menggunakan kata kerja %w dan tambahkan kontes 'config:' (dengan jalur).
Solusi tersedia setelah 3 upaya
Kesalahan AS. Apakah untuk memverifikasi apakah ada kesalahan dalam bungkus yang sesuai dengan penjaga ErrNotFound dan cap 'manca!' dalam hal ini.
Solusi tersedia setelah 3 upaya
Kapan dan secara idiomatis menggunakan kepanikan di perpustakaan Go?
func F(x int) {
// ??? panic(...)
}