Chapter 4 of 10
Setelah bab ini, Anda mampu:
defer untuk cleanup yang dapat diandalkan.Fungsi Go menyatakan tipe setiap parameter dan hasil. Parameter bertipe sama dapat diringkas.
package main
import "fmt"
func tambah(a, b int) int {
return a + b
}
func bagi(a, b float64) (float64, bool) {
if b == 0 {
return 0, false
}
return a / b, true
}
func main() {
fmt.Println(tambah(2, 3))
hasil, ok := bagi(10, 2)
fmt.Println(hasil, ok)
}
Beberapa hasil umum dipakai untuk nilai beserta status atau error. Named result tersedia, tetapi hasil eksplisit biasanya lebih jelas pada fungsi pendek. Fungsi variadik menerima nol atau lebih argumen:
func jumlah(angka ...int) int {
total := 0
for _, n := range angka {
total += n
}
return total
}
Fungsi adalah nilai dan dapat disimpan atau dikirim sebagai argumen:
func terapkan(a, b int, operasi func(int, int) int) int {
return operasi(a, b)
}
Argumen selalu diteruskan by value. Jika argumennya pointer, nilai alamat itulah yang disalin, sehingga fungsi dapat mengubah objek tujuan.
package main
import "fmt"
func naikkan(n *int) {
*n++
}
func main() {
nilai := 10
naikkan(&nilai)
fmt.Println(nilai)
}
&nilai mengambil alamat; *n membaca atau menulis nilai yang ditunjuk. Go tidak mendukung aritmetika pointer. Pointer memiliki zero value nil; dereference pointer nil menyebabkan panic.
Gunakan pointer ketika fungsi perlu memutasi nilai pemanggil, ketika penyalinan struct besar memang mahal, atau ketika nil bermakna. Jangan memakai pointer hanya agar kode terlihat efisien. Slice dan map sudah memiliki descriptor/header yang merujuk data internal; fungsi biasanya tidak memerlukan pointer ke slice atau map untuk mengubah elemennya.
error adalah interface standar:
type error interface {
Error() string
}
Fungsi yang dapat gagal lazimnya mengembalikan error terakhir. Pemeriksaan dilakukan segera.
package main
import (
"errors"
"fmt"
)
func akarSederhana(n float64) (float64, error) {
if n < 0 {
return 0, errors.New("nilai tidak boleh negatif")
}
if n == 0 {
return 0, nil
}
x := n
for i := 0; i < 10; i++ {
x = (x + n/x) / 2
}
return x, nil
}
func main() {
hasil, err := akarSederhana(9)
if err != nil {
fmt.Println("gagal:", err)
return
}
fmt.Println(hasil)
}
Tambahkan konteks tanpa menghilangkan identitas error memakai %w:
return fmt.Errorf("membaca konfigurasi %q: %w", nama, err)
Periksa rantai error dengan errors.Is, bukan membandingkan teks. Gunakan errors.As untuk mengambil tipe error tertentu.
package main
import (
"errors"
"fmt"
)
var ErrTidakDitemukan = errors.New("data tidak ditemukan")
func cari(id int) (string, error) {
if id != 1 {
return "", fmt.Errorf("id %d: %w", id, ErrTidakDitemukan)
}
return "data", nil
}
func main() {
_, err := cari(2)
if errors.Is(err, ErrTidakDitemukan) {
fmt.Println("gunakan hasil kosong")
}
}
Alur penanganannya:
deferdefer menjadwalkan pemanggilan sampai fungsi di sekitarnya selesai, baik melalui return normal maupun panic. Argumen deferred call dievaluasi saat defer dijalankan. Banyak defer dieksekusi LIFO.
package main
import "fmt"
func urutan() {
defer fmt.Println("pertama dijadwalkan, terakhir berjalan")
defer fmt.Println("kedua dijadwalkan, pertama berjalan")
fmt.Println("body")
}
func main() {
urutan()
}
Pola cleanup file:
func bacaAwal(nama string) ([]byte, error) {
f, err := os.Open(nama)
if err != nil {
return nil, fmt.Errorf("membuka %q: %w", nama, err)
}
defer f.Close()
data := make([]byte, 64)
n, err := f.Read(data)
if err != nil && !errors.Is(err, io.EOF) {
return nil, fmt.Errorf("membaca %q: %w", nama, err)
}
return data[:n], nil
}
Import yang diperlukan adalah errors, fmt, io, dan os. Untuk operasi tulis, error dari Close dapat penting; jangan selalu mengabaikannya. Hindari defer di loop panjang jika resource harus dilepas pada setiap iterasi; pindahkan satu iterasi ke fungsi kecil.
_. Abaikan hanya bila kegagalan benar-benar tidak relevan dan kontraknya dipahami.err.Error() sebagai teks. Gunakan errors.Is atau errors.As.%w. %v memutus kemampuan pemeriksaan rantai.nil bila termasuk input yang mungkin.defer sebelum memastikan resource berhasil dibuka. Jadwalkan cleanup setelah pemeriksaan error.defer, bukan saat eksekusi akhir.tukar(a, b *int) dan uji perubahan pada pemanggil.errors.Is.defer, lalu jalankan untuk memverifikasi urutan LIFO.Fungsi Go mendukung beberapa hasil, nilai fungsi, dan parameter variadik. Semua argumen disalin; pointer memungkinkan mutasi terhadap nilai tujuan, tetapi harus digunakan dengan alasan jelas. Error adalah nilai yang diperiksa, diberi konteks dengan %w, dan dikenali melalui errors.Is/errors.As. defer menempatkan cleanup dekat akuisisi resource dan berjalan dalam urutan LIFO.