Defer, Panic, Recover
Tiga mekanisme Go untuk cleanup dan error luar biasa. Kombinasi ketiganya menggantikan peran try/finally/catch di bahasa lain — tapi dengan filosofi yang berbeda.
Defer
defer menunda eksekusi sebuah statement sampai fungsi yang memuatnya selesai (baik normal return, panic, atau early return). Argumen dievaluasi saat defer ditulis, bukan saat dijalankan.
func contoh() {
fmt.Println("1. mulai")
defer fmt.Println("3. defer jalan saat fungsi selesai")
fmt.Println("2. body")
}
// Output:
// 1. mulai
// 2. body
// 3. defer jalan saat fungsi selesai
LIFO — Last In, First Out
Kalau ada banyak defer, mereka dieksekusi dalam urutan terbalik (seperti tumpukan piring).
func main() {
defer fmt.Println("1")
defer fmt.Println("2")
defer fmt.Println("3")
fmt.Println("start")
}
// Output:
// start
// 3
// 2
// 1
Pola Umum Defer
// 1. Tutup file setelah dibuka
f, err := os.Open("data.txt")
if err != nil { return err }
defer f.Close() // pasti dipanggil saat fungsi exit
// 2. Unlock mutex
mu.Lock()
defer mu.Unlock()
// 3. Stop timer / cleanup
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
Keuntungan: cleanup code berdampingan dengan setup, gampang dibaca. Tidak lupa close meski ada early return atau panic di tengah fungsi.
Panic
panic menghentikan eksekusi normal dan unwind stack — menjalankan semua defer yang tertumpuk sepanjang jalan ke atas, lalu crash program.
func bagi(a, b int) int {
if b == 0 {
panic("pembagian dengan nol!")
}
return a / b
}
bagi(10, 0)
// panic: pembagian dengan nol!
// goroutine 1 [running]:
// ... stack trace ...
// exit status 2
Kapan Panic vs Return Error?
Gunakan error | Gunakan panic |
|---|---|
| Input user invalid | Programmer error / bug |
| File tidak ditemukan | Nil pointer dereference yang tidak mungkin terjadi |
| Network timeout | Array out-of-bounds saat logika seharusnya mencegahnya |
| Validasi gagal | Init fail yang membuat program tak bisa lanjut (startup) |
Rule of thumb: 99% error di Go harus jadi nilai return biasa. Panic hanya untuk kondisi yang benar-benar mustahil dalam keadaan sehat — atau saat program tidak punya cara melanjutkan sama sekali.
Recover
recover menangkap panic dan mengembalikan kontrol ke program. Hanya bekerja di dalam deferred function.
func safeCall() {
defer func() {
if r := recover(); r != nil {
fmt.Println("Ditangkap:", r)
}
}()
panic("masalah besar!")
}
safeCall()
fmt.Println("program lanjut") // ← tetap tercetak
Cleanup dengan Defer (Resource Pattern)
func prosesFile(path string) error {
f, err := os.Open(path)
if err != nil {
return fmt.Errorf("buka file: %w", err)
}
defer f.Close() // pasti ditutup, apapun yang terjadi
data, err := io.ReadAll(f)
if err != nil {
return err // f.Close() tetap jalan
}
// proses data...
return nil
}
Catatan Penting
- Argumen defer dievaluasi saat ditulis —
defer fmt.Println(x)mencetak nilaixsaat itu, bukan saat defer jalan - Recover hanya efektif di deferred func — di tempat lain akan return nil
- Panic lintas goroutine tidak bisa direcover dari goroutine lain — setiap goroutine harus handle panic-nya sendiri
- Jangan gunakan recover sebagai "except" — ini bukan exception handling. Gunakan hanya untuk konversi panic → error di batas library publik