Di Go, kegagalan bukan sesuatu yang "dilempar" lalu ditangkap try/catch. Kegagalan adalah nilai biasa bertipe error, yang dikembalikan fungsi sebagai hasil terakhirnya. Bayangin kasir Warung Bakso Mas Bimo menerima uang yang kurang. Kasir gak membanting meja; ia menyerahkan kembalian Rp0 bersama secarik catatan: "uang kurang". Yang menerima wajib membaca catatan itu dulu sebelum percaya pada kembaliannya.
🧪 Tebakan cepat:
kembalian, _ := bayar(32000, "20000")lalufmt.Println("kembalian:", kembalian). Uangnya kurang. Program berhenti dengan error, atau tenang-tenang mencetakkembalian: 0? Tebak dulu, jawabannya ada di bagian "Jangan buang error dengan _".
error itu interface
error adalah interface bawaan Go dengan satu method:
type error interface {
Error() string
}
Tipe apa pun yang punya Error() string adalah sebuah error. Nilai nil artinya gak ada error. Cara paling gampang membuatnya: errors.New("uang kurang") dari package errors.
Pola if err != nil
package main
import (
"errors"
"fmt"
"strconv"
)
var ErrUangKurang = errors.New("uang kurang")
func bayar(total int, input string) (int, error) {
uang, err := strconv.Atoi(input)
if err != nil {
return 0, fmt.Errorf("baca uang %q: %w", input, err)
}
if uang < total {
return 0, fmt.Errorf("bayar %d dari %d: %w", uang, total, ErrUangKurang)
}
return uang - total, nil
}
func main() {
kembalian, err := bayar(32000, "50000")
if err != nil {
fmt.Println("gagal:", err)
return
}
fmt.Println("kembalian:", kembalian)
}
strconv.Atoi(input)(packagestrconv, string conversion) mengubah teks yang diketik kasir jadiint. Ia mengembalikan(int, error): teks yang bukan angka memberi error.- Tiap langkah yang bisa gagal langsung dicek:
if err != nil { return ... }. Begitu ada masalah, fungsi keluar saat itu juga dengan zero value (0) dan error-nya. Kalau semuanya lancar, baris terakhir mengembalikan hasilnya dannil. - Di
main, error dicek sebelumkembaliandipakai. Cabang error mencetak lalureturn, jadi baris sesudahnya gak jalan.
input |
err |
Tercetak |
|---|---|---|
"50000" |
nil |
kembalian: 18000 |
"32000" |
nil |
kembalian: 0 |
"20000" |
uang kurang | gagal: bayar 20000 dari 32000: uang kurang |
"50rb" |
bukan angka | gagal: baca uang "50rb": strconv.Atoi: parsing "50rb": invalid syntax |
Contoh kecil yang sama: _, err := strconv.Atoi("abc") gagal karena "abc" bukan angka, jadi if err != nil { fmt.Println("error") } mencetak error.
Di latihan: safeDivide
Fungsi safeDivide(a, b float64) (float64, error) sudah ada di kode awal: kalau b == 0 ia mengembalikan errors.New("tidak bisa membagi dengan nol"), selain itu a / b, nil. Tugasmu ada di main: terima dua hasilnya, lalu cek dengan if err != nil.
3.3333333333333335
Error: tidak bisa membagi dengan nol
safeDivide(10, 3) memberi 3.3333333333333335 (begitulah float64 mencetak 10/3), dan safeDivide(10, 0) memberi error yang pesannya dicetak.
errors.New atau fmt.Errorf
errors.New("uang kurang")membuat error dengan pesan tetap. Error yang mau dicek pemanggil biasanya disimpan di variabel package dengan awalanErr:var ErrUangKurang = errors.New("uang kurang").fmt.Errorfmenyusun pesan dengan verb sepertiPrintf, jadi bisa menambah keterangan:bayar 20000 dari 32000.- Verb
%w(wrap) membungkus error aslinya di dalam error baru. Pesannya tetap tersambung, dan error aslinya masih bisa ditemukan.
errors.Is(err, ErrUangKurang) menelusuri bungkusan itu dan menjawab apakah ErrUangKurang ada di dalamnya:
fmt.Println(errors.Is(err, ErrUangKurang)) // true dengan %w, false dengan %v
Dengan %v, pesannya persis sama, tapi yang disalin cuma teksnya. Error aslinya hilang, jadi errors.Is menjawab false.
📌 errors.As: ambil detail dari error
errors.As mencari error dengan tipe tertentu di dalam bungkusan, lalu mengisinya ke variabel:
var numErr *strconv.NumError
if errors.As(err, &numErr) {
fmt.Println(numErr.Func, numErr.Num) // Atoi 50rb
}
Untuk input "50rb", error dari strconv.Atoi bertipe *strconv.NumError, jadi numErr.Num berisi teks yang gagal dibaca. Membuat tipe error sendiri ada di lesson berikutnya, Custom Errors.
Jangan buang error dengan _
kembalian, _ := bayar(32000, "20000")
fmt.Println("kembalian:", kembalian)
kembalian: 0
Gak ada error yang muncul. Itu jawaban tebakannya: program tenang-tenang mencetak kembalian: 0, padahal pembeli belum lunas. Error di Go gak menghentikan apa pun sendiri; kalau dibuang, peringatannya hilang begitu saja.
⚠️ Jebakan yang sering ketemu
- Memakai hasil sebelum mengecek
err. Kalauerr != nil, hasilnya cuma zero value. Cek dulu, baru pakai. %vpadahal maksudnya%w. Pesannya sama, tapierrors.Isdanerrors.Asgak bisa lagi menemukan error aslinya.- Membandingkan teks pesan.
err.Error() == "uang kurang"rapuh, dan duaerrors.New("uang kurang")bukan error yang sama (a == bbernilaifalse). Simpan sekali diErrUangKurang, lalu pakaierrors.Is. errditerima tapi gak dicek.uang, err := strconv.Atoi(...)tanpa memakaierrgagal compile dengandeclared and not used: err. Itu tanda kamu lupa menanganinya.
TL;DR: Error di Go adalah nilai bertipe error, interface dengan method Error() string, dikembalikan sebagai hasil terakhir; nil artinya gak ada error. Polanya: hasil, err := f(), lalu if err != nil { ... return } sebelum hasilnya dipakai. errors.New membuat error dengan pesan tetap, fmt.Errorf menambah keterangan, dan %w membungkus error aslinya supaya errors.Is(err, ErrUangKurang) dan errors.As(err, &numErr) masih menemukannya; %v cuma menyalin teks. strconv.Atoi("abc") mengembalikan error. Di latihan, safeDivide(10, 3) mencetak 3.3333333333333335 dan safeDivide(10, 0) mencetak tidak bisa membagi dengan nol. Jangan buang error dengan _.