Setiap variabel di Go punya zero value, dan zero value untuk slice maupun map adalah nil — penunjuk kosong yang belum menunjuk penyimpanan mana pun. Masalahnya, keduanya tidak berperilaku sama: slice nil masih bisa ditumbuhkan dengan append, sedangkan map nil hanya boleh dibaca dan langsung panic ketika ditulis. Perbedaan inilah yang membingungkan pemula dan memicu galat assignment to entry in nil map yang terkenal. Pada chapter ini kita membedah batas operasi slice dan map nil supaya program Anda tidak pernah berhenti karena panic ini.
Slice nil Masih Bisa Dipakai #
Deklarasi var s []int tanpa make maupun literal menghasilkan slice nil. Tiga operasi tetap sah untuk slice nil: len(s) menghasilkan 0, pembanding s == nil bernilai true, dan append(s, elemen) bekerja normal — append otomatis mengalokasikan backing array baru ketika dibutuhkan. Akibatnya pola deklarasi kosong lalu mengisi bertahap aman dilakukan tanpa inisialisasi khusus.
Slice kosong hasil literal []int{} berbeda dari slice nil: kosong == nil bernilai false karena slice tersebut sudah menunjuk penyimpanan (walau berisi nol elemen). Keduanya sama-sama mencetak [] dan ber-len 0, sehingga satu-satunya pembeda yang andal adalah pembanding == nil — slice hanya boleh dibandingkan dengan nil, bukan dengan slice lain.
Map nil Boleh Dibaca, Tidak Boleh Ditulis #
Deklarasi var m map[string]int menghasilkan map nil. Pembacaan m["budi"] tetap sah dan mengembalikan zero value tipe nilai (0) tanpa galat, len(m) menghasilkan 0, dan comma-ok mengembalikan false untuk key apa pun. Batasnya ada pada penulisan: pernyataan m["budi"] = 25 pada map nil memicu panic runtime assignment to entry in nil map — map nil tidak punya struktur internal untuk menyimpan pasangan, dan runtime memilih menghentikan program alih-alih mengabaikan penulisan.
Berkas error-nil-map.go di bundle chapter ini sengaja memuat penulisan tersebut. Jalankan dengan go run error-nil-map.go; program berhenti dengan panic:
package main
func main() {
var m map[string]int
// Menulis ke map nil memicu panic saat runtime.
m["budi"] = 25
}Output yang Dihasilkan:
panic: assignment to entry in nil mapSetelah baris pesan tersebut, runtime juga mencetak jejak stack goroutine 1 [running]: yang memuat path absolut berkas dan alamat memori, lalu go run menutup sesi dengan laporan exit status 2 — program berhenti dengan exit code 2 dan seluruh baris setelah panic tidak sempat dieksekusi. Bagian jejak stack tidak disalin penuh ke blok output karena isinya berbeda di setiap komputer; baris panic: itulah pesan yang pasti sama.
Solusi: Inisialisasi Sebelum Menulis #
Dua cara menyiapkan map yang siap ditulis sudah kita kenal pada chapter Map
: make(map[string]int) mengalokasikan map kosong yang siap diisi, dan literal map[string]int{} memberi efek sama untuk kasus sederhana. Keduanya membuat m == nil bernilai false sehingga penulisan aman.
Program Utuh #
Berkas nil-demo.go membuktikan seluruh klaim di atas secara berurutan. Jalankan dengan go run nil-demo.go dari direktori chapter:
package main
import "fmt"
func main() {
// Slice nil: deklarasi tanpa make maupun literal.
var s []int
fmt.Println("s :", s)
fmt.Println("s == nil :", s == nil)
fmt.Println("len(s) :", len(s))
// append pada nil slice diperbolehkan: slice tumbuh otomatis.
s = append(s, 10, 20)
fmt.Println("setelah append:", s)
fmt.Println("s == nil :", s == nil)
// Slice kosong bukan nil: nol elemen tetapi sudah dialokasikan.
kosong := []int{}
fmt.Println("kosong :", kosong)
fmt.Println("kosong == nil:", kosong == nil)
// Map nil: pembacaan tetap sah dan menghasilkan zero value.
var m map[string]int
fmt.Println("m :", m)
fmt.Println("m == nil :", m == nil)
fmt.Println("len(m) :", len(m))
fmt.Println(`m["budi"] :`, m["budi"])
// Solusi agar bisa menulis: buat map dengan make.
siap := make(map[string]int)
siap["budi"] = 25
fmt.Println("siap :", siap)
}Output yang Dihasilkan:
s : []
s == nil : true
len(s) : 0
setelah append: [10 20]
s == nil : false
kosong : []
kosong == nil: false
m : map[]
m == nil : true
len(m) : 0
m["budi"] : 0
siap : map[budi:25]Mari kita telaah baris demi baris program di atas:
- Baris 6–9 (
var s []int): slice nil tercetak[], pembandings == nilbernilaitrue, danlen(s)tetap sah menghasilkan 0. - Baris 12–14 (
s = append(s, 10, 20)):appendpada slice nil berhasil; hasil reassign membuatsberisi[10 20]dans == nilberubah menjadifalse. - Baris 17–19 (
kosong := []int{}): slice kosong tampil sama seperti slice nil, tetapikosong == nilbernilaifalse— bukti keduanya berbeda. - Baris 22–27 (
var m map[string]int): map nil tercetakmap[]denganlen0, dan pembacaanm["budi"]menghasilkan zero value 0 tanpa panic. - Baris 30–32 (
siap := make(map[string]int)): map hasilmakesiap ditulis;siap["budi"] = 25berjalan mulus dan isi tercetakmap[budi:25].
Kita kini memahami seluruh perilaku array, slice, dan map beserta jebakannya. Satu pertanyaan praktis tinggal dijawab: di antara ketiganya, struktur mana yang paling tepat untuk masalah Anda? Chapter penutup section ini membandingkan ketiganya dalam satu tabel keputusan.
Referensi #
- Go Blog: Slices: usage and internals (slices) — perilaku nil pada slice.
- Go Blog: Maps in action (maps) — catatan resmi map nil.
- Spesifikasi Go: Making slices, maps and channels
— aturan
makedanniluntuk slice serta map.
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
error-nil-map.go
Unduh berkaspackage main
func main() {
var m map[string]int
// Menulis ke map nil memicu panic saat runtime.
m["budi"] = 25
}
nil-demo.go
Unduh berkaspackage main
import "fmt"
func main() {
// Slice nil: deklarasi tanpa make maupun literal.
var s []int
fmt.Println("s :", s)
fmt.Println("s == nil :", s == nil)
fmt.Println("len(s) :", len(s))
// append pada nil slice diperbolehkan: slice tumbuh otomatis.
s = append(s, 10, 20)
fmt.Println("setelah append:", s)
fmt.Println("s == nil :", s == nil)
// Slice kosong bukan nil: nol elemen tetapi sudah dialokasikan.
kosong := []int{}
fmt.Println("kosong :", kosong)
fmt.Println("kosong == nil:", kosong == nil)
// Map nil: pembacaan tetap sah dan menghasilkan zero value.
var m map[string]int
fmt.Println("m :", m)
fmt.Println("m == nil :", m == nil)
fmt.Println("len(m) :", len(m))
fmt.Println(`m["budi"] :`, m["budi"])
// Solusi agar bisa menulis: buat map dengan make.
siap := make(map[string]int)
siap["budi"] = 25
fmt.Println("siap :", siap)
}