Error Handling

Error handling di Go menggunakan tipe error sebagai nilai return, bukan exception seperti bahasa lain. Ini membuat penanganan error lebih eksplisit dan terkontrol.

Contoh Masalah

Bagaimana cara:

  1. Menangani error dengan benar
  2. Membuat custom error
  3. Mengelola error di berbagai situasi

Penyelesaian

package main

import (
    "errors"
    "fmt"
    "math"
    "os"
    "strconv"
)

// 1. Custom error type
type DivisionError struct {
    dividend float64
    divisor  float64
    message  string
}

func (e *DivisionError) Error() string {
    return fmt.Sprintf("%s: %f / %f", e.message, e.dividend, e.divisor)
}

// 2. Function yang mengembalikan error
func divide(x, y float64) (float64, error) {
    if y == 0 {
        return 0, &DivisionError{
            dividend: x,
            divisor:  y,
            message:  "tidak bisa membagi dengan nol",
        }
    }
    return x / y, nil
}

// 3. Function dengan multiple error cases
func sqrt(x float64) (float64, error) {
    if x < 0 {
        return 0, errors.New("tidak bisa menghitung akar dari angka negatif")
    }
    return math.Sqrt(x), nil
}

// 4. Wrapping errors
type ValidationError struct {
    Field string
    Err   error
}

func (e *ValidationError) Error() string {
    return fmt.Sprintf("validasi gagal pada field %s: %v", e.Field, e.Err)
}

func (e *ValidationError) Unwrap() error {
    return e.Err
}

// 5. Function yang menggunakan wrapped error
func validateAge(age string) error {
    n, err := strconv.Atoi(age)
    if err != nil {
        return &ValidationError{
            Field: "age",
            Err:   err,
        }
    }
    if n < 0 || n > 150 {
        return &ValidationError{
            Field: "age",
            Err:   errors.New("umur harus antara 0 dan 150"),
        }
    }
    return nil
}

func main() {
    // Contoh 1: Basic error handling
    result, err := divide(10, 0)
    if err != nil {
        fmt.Printf("Error: %v\n", err)
    } else {
        fmt.Printf("Hasil: %f\n", result)
    }

    // Contoh 2: Multiple error cases
    numbers := []float64{16, -4, 25, 0}
    fmt.Printf("\nMenghitung akar:\n")
    for _, num := range numbers {
        result, err := sqrt(num)
        if err != nil {
            fmt.Printf("Error untuk %f: %v\n", num, err)
            continue
        }
        fmt.Printf("Akar dari %f adalah %f\n", num, result)
    }

    // Contoh 3: File operations dengan error handling
    fmt.Printf("\nMencoba membaca file:\n")
    content, err := os.ReadFile("nonexistent.txt")
    if err != nil {
        if os.IsNotExist(err) {
            fmt.Println("File tidak ditemukan")
        } else {
            fmt.Printf("Error lain: %v\n", err)
        }
    } else {
        fmt.Printf("Konten file: %s\n", content)
    }

    // Contoh 4: Validation errors
    fmt.Printf("\nValidasi umur:\n")
    ages := []string{"25", "-5", "200", "abc"}
    for _, age := range ages {
        err := validateAge(age)
        if err != nil {
            var validErr *ValidationError
            if errors.As(err, &validErr) {
                fmt.Printf("Validation error: %v\n", validErr)
            } else {
                fmt.Printf("Unexpected error: %v\n", err)
            }
            continue
        }
        fmt.Printf("Umur %s valid\n", age)
    }

    // Contoh 5: Error wrapping dan unwrapping
    fmt.Printf("\nError wrapping:\n")
    err = validateAge("abc")
    if err != nil {
        fmt.Printf("Original error: %v\n", err)
        if wrapped := errors.Unwrap(err); wrapped != nil {
            fmt.Printf("Wrapped error: %v\n", wrapped)
        }
    }
}

Penjelasan Kode

  1. Basic Error Handling

    • Return error sebagai nilai kedua
    • Cek error dengan if
    • Nil berarti tidak ada error
  2. Custom Error

    • Implement interface Error()
    • Bisa tambah informasi tambahan
    • Lebih deskriptif
  3. Error Wrapping

    • Menambah konteks ke error
    • Bisa di-unwrap
    • Chain of responsibility

Output

Error: tidak bisa membagi dengan nol: 10.000000 / 0.000000

Menghitung akar:
Akar dari 16.000000 adalah 4.000000
Error untuk -4.000000: tidak bisa menghitung akar dari angka negatif
Akar dari 25.000000 adalah 5.000000
Akar dari 0.000000 adalah 0.000000

Mencoba membaca file:
File tidak ditemukan

Validasi umur:
Umur 25 valid
Validation error: validasi gagal pada field age: umur harus antara 0 dan 150
Validation error: validasi gagal pada field age: umur harus antara 0 dan 150
Validation error: validasi gagal pada field age: strconv.Atoi: parsing "abc": invalid syntax

Error wrapping:
Original error: validasi gagal pada field age: strconv.Atoi: parsing "abc": invalid syntax
Wrapped error: strconv.Atoi: parsing "abc": invalid syntax

Tips

  • Selalu periksa error yang dikembalikan
  • Buat custom error yang informatif
  • Gunakan error wrapping untuk konteks
  • Jangan ignore error
  • Log error dengan informasi yang cukup