Defer, Panic, Recover — Go

Defer, Panic, Recover Tiga mekanisme Go untuk cleanup dan error luar biasa. Kombinasi ketiganya menggantikan peran try/finally/catch di bahasa lain — tapi…

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
Diagram urutan eksekusi defer dengan model stack LIFO Urutan PUSH (saat defer) defer A() ← first defer B() defer C() ← last ↓ fungsi return ↓ Urutan POP (saat run) C() jalan ← first B() jalan A() jalan ← last LIFO: defer terakhir = dieksekusi pertama Analogi: tumpukan piring — piring terakhir ditaruh adalah yang pertama diambil saat dicuci.
Defer statement ditumpuk di stack internal dan dieksekusi terbalik saat fungsi selesai.

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 errorGunakan panic
Input user invalidProgrammer error / bug
File tidak ditemukanNil pointer dereference yang tidak mungkin terjadi
Network timeoutArray out-of-bounds saat logika seharusnya mencegahnya
Validasi gagalInit 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

Yang akan kamu pelajari