x.(T): mengambil nilai dari interface
Nilai interface menyembunyikan tipe konkret yang ada di dalamnya. Type assertion x.(T) meminta tipe itu kembali.
Output:
gopher 6
0 false
string of length 6
x harus bertipe interface. Assertion pada tipe konkret gagal dikompilasi: invalid operation: s (variable of type string) is not an interface.
Bentuk satu nilai bisa panic
Kalau assertion-nya salah, bentuk satu nilai akan panic dengan pesan yang menyebut kedua tipe:
Output:
recovered: interface conversion: interface {} is int, not string
Pakai bentuk satu nilai hanya kalau tipe yang berbeda berarti kesalahan pemrograman yang memang ingin kamu hentikan dengan crash. Di tempat lain, pakai comma-ok. Saat gagal, comma-ok memberi zero value dari T dan false.
Assertion pada interface yang nil juga gagal: var v any; v.(string) akan panic dengan interface conversion: interface {} is nil, not string, dan bentuk comma-ok mengembalikan "", false.
Assertion ke interface lain
T boleh berupa tipe interface. Assertion-nya lalu berhasil jika nilai dinamisnya mengimplementasikan T, dan hasilnya tetap menyimpan nilai dinamis yang sama. Dengan cara inilah pustaka standar memeriksa kemampuan opsional.
io.Copy memeriksa apakah sumbernya mengimplementasikan io.WriterTo dan memakainya kalau iya. fmt memeriksa Stringer dan error. Pola ini membuat interface kecil tetap kecil, sementara pemanggil tetap mendapat manfaat dari implementasi yang lebih kaya.
Type switch
Ketika sebuah nilai bisa berupa salah satu dari beberapa tipe, type switch menggantikan rangkaian assertion. x.(type) hanya sah di dalam switch.
Output:
hi
integer 7
integer 8
3.14
nil
error: boom
unhandled []int
Aturan yang perlu kamu tahu:
- Di case dengan satu tipe,
xbertipe itu. Di case yang berisi beberapa tipe, dan didefault,xbertipe interface aslinya (anydi sini). - Case diperiksa berurutan. Taruh interface yang lebih spesifik sebelum yang lebih umum, karena sebuah nilai bisa memenuhi beberapa interface.
case nilhanya cocok dengan interface yang nil, bukan interface yang berisi pointer nil.- Tidak ada
fallthroughdi type switch.
errors.As: assertion untuk error yang dibungkus
Error sering dibungkus dengan konteks: fmt.Errorf("load config: %w", err). Type assertion langsung hanya melihat error terluar dan melewatkan error di dalamnya. errors.As menelusuri rantainya.
Output:
type assertion finds it: false
errors.As finds it: open /no/such/file
Pakai type assertion pada error hanya kalau kamu tahu error-nya tidak dibungkus, yang dalam praktiknya hampir tidak pernah. Halaman custom error membahas errors.As, errors.Is, dan cara mendefinisikan tipe error sendiri.
Assertion vs konversi vs generic
| Kamu punya | Kamu ingin | Pakai |
|---|---|---|
int | float64 | konversi: float64(n) |
any yang berisi int | int-nya | assertion: v.(int) |
any dengan beberapa kemungkinan tipe | bercabang berdasarkan tipe | type switch |
error yang mungkin dibungkus | tipe error tertentu | errors.As |
| fungsi yang bekerja untuk banyak tipe | keamanan saat kompilasi | generic |
Kode yang penuh any dan type switch sering menjadi tanda bahwa generic atau interface yang tepat akan menyatakan maksudnya dengan lebih baik dan menangkap kesalahan saat kompilasi.
Kesalahan umum
- Memakai bentuk yang bisa panic pada data yang tidak tepercaya. JSON hasil decode, nilai map bertipe
any, input plugin: selalu pakai comma-ok. - Assertion ke tipe angka yang salah. Angka JSON di-decode ke
anysebagaifloat64, jadiv.(int)gagal pada angka itu. - Assertion ke tipe nilai padahal yang disimpan adalah pointer. Kalau interface-nya berisi
*User, makav.(User)gagal. Lakukan assertionv.(*User). - Type assertion pada error. Pakai
errors.As.
Pertanyaan yang sering diajukan
Apa itu type assertion di Go?
Ekspresi x.(T) dengan x berupa nilai interface. Jika T adalah tipe konkret, ekspresi ini mengembalikan nilai yang disimpan di x sebagai T. Jika T adalah tipe interface, ekspresi ini memeriksa apakah nilai yang disimpan juga mengimplementasikan T. Bentuk dengan satu nilai akan panic kalau pemeriksaannya gagal; bentuk dengan dua nilai, v, ok := x.(T), melaporkannya di ok.
Bagaimana cara memeriksa tipe sebuah nilai interface di Go?
Pakai type switch: switch v := x.(type) { case int: ...; case string: ...; default: ... }. Di dalam setiap case, v bertipe sesuai case itu. Untuk satu tipe saja, assertion comma-ok s, ok := x.(string) lebih singkat. fmt.Printf("%T", x) mencetak nama tipenya untuk keperluan debugging.
Apa bedanya type assertion dan konversi tipe di Go?
Konversi T(x) mengubah nilai dari satu tipe ke tipe lain, seperti float64(n); compiler yang memutuskan apakah konversi itu diizinkan, dan konversi tidak bisa gagal saat program berjalan. Assertion x.(T) hanya bekerja pada nilai interface dan memeriksa saat program berjalan tipe apa yang tersimpan di dalamnya. Kamu tidak bisa menulis int(x) untuk any yang berisi int; kamu butuh x.(int).
Apakah sebaiknya saya memakai type assertion untuk memeriksa tipe error?
Tidak. Pakai errors.As(err, &target). Assertion langsung err.(*MyError) hanya melihat error terluar, jadi assertion itu gagal begitu error-nya dibungkus dengan fmt.Errorf("...: %w", err). errors.As menelusuri rantai pembungkusnya.