Data yang masuk ke any atau interface lain pada akhirnya harus keluar kembali dalam bentuk tipe konkret: angka yang dijumlahkan, teks yang dipotong, struct yang field-nya dibaca. Operasi mengeluarkan nilai dari interface dinamakan type assertion. Bentuknya kecil — satu pasang tanda kurung setelah nilai interface — tetapi konsekuensinya besar: satu bentuk assertion bisa menghentikan program lewat panic, sementara bentuk lainnya aman sepenuhnya. Chapter ini membedah kedua bentuk tersebut, memperlihatkan panic-nya secara nyata, lalu menunjukkan assertion yang menyasar interface, bukan hanya tipe konkret.

Dua Bentuk Type Assertion #

Bentuk pertama mengembalikan satu nilai:

go
v := x.(int)

Statement ini menyatakan: “dynamic type dari x adalah int, keluarkan dynamic value-nya”. Jika dugaan benar, v berisi nilai integer siap pakai. Jika dynamic type ternyata string, program panic saat runtime — bukan gagal kompilasi, karena compiler tidak tahu isi pasangan (dynamic type, dynamic value) sebelum program berjalan.

Bentuk kedua, comma-ok, mengembalikan dua nilai:

go
v, ok := x.(int)

Dalam bentuk ini kegagalan tidak lagi mematikan program: ok bernilai false dan v diisi zero value tipe sasaran (0 untuk int). Pola ini semangatnya sama dengan pemeriksaan elemen map v, ok := m[k] dan penerimaan channel v, ok := <-ch, sehingga terasa alami dikombinasikan dengan if:

go
if v, ok := x.(int); ok {
	// v hanya hidup di dalam blok ini, dengan tipe int yang sudah pasti
}

Panic di Dunia Nyata #

Berkas error-assertion.go sengaja memakai bentuk satu nilai dengan tipe yang salah. Jalankan berkas tersebut dan perhatikan apa yang terjadi:

go
package main

import "fmt"

func main() {
	var nilai any = "bukan angka"

	// Bentuk satu nilai: tipe tidak cocok menyebabkan panic saat runtime.
	angka := nilai.(int)
	fmt.Println("baris ini tidak pernah tercetak:", angka)
}

Output yang Dihasilkan:

text
panic: interface conversion: interface {} is string, not int

exit status 2

Di antara kedua baris tersebut Go juga mencetak goroutine 1 [running]: beserta stack trace yang memuat path berkas dan offset memori; bagian itu dihilangkan karena isinya berbeda di setiap mesin. Proses berhenti dengan exit status 2 dan fmt.Println di baris terakhir tidak pernah dieksekusi — panic memutus alur di titik assertion terjadi. Pesan interface conversion menyebutkan dua fakta penting: dynamic type sebenarnya (string) dan tipe yang diduga (int).

Assertion ke Interface #

Sasaran assertion tidak harus tipe konkret. x.(Stringer) memeriksa apakah dynamic type dari x mengimplementasikan interface Stringer; jika ya, hasil assertion bertipe Stringer sehingga method String() bisa dipanggil tanpa tahu struct aslinya. Kegagalan tetap mengikuti aturan yang sama: panic pada bentuk satu nilai, ok bernilai false pada bentuk comma-ok. Teknik ini menjadi tulang punggung pemeriksaan seperti errors.As yang berburu tipe error tertentu di dalam rantai error.

Program Demo #

Berkas assertion-demo.go merangkum bentuk comma-ok, kombinasi if, dan assertion ke interface dalam satu program:

go
package main

import "fmt"

// Stringer adalah tiruan kecil dari fmt.Stringer.
type Stringer interface {
	String() string
}

type Sensor struct {
	Nama string
	Suhu float64
}

func (s Sensor) String() string {
	return fmt.Sprintf("%s: %.1f derajat", s.Nama, s.Suhu)
}

func main() {
	var nilai any = Sensor{Nama: "Ruang Server", Suhu: 21.5}

	// Bentuk comma-ok: aman, ok bernilai false tanpa panic.
	sensor, ok := nilai.(Sensor)
	fmt.Println("ok:", ok, "sensor:", sensor)

	// Kombinasi comma-ok dalam if untuk cabang yang hanya dipakai bila cocok.
	if s, ok := nilai.(Sensor); ok {
		fmt.Println("suhu ruangan:", s.Suhu)
	} else {
		fmt.Println("nilai bukan Sensor")
	}

	// Type assertion ke interface: apakah tipe dinamis mengimplementasikan Stringer?
	if s, ok := nilai.(Stringer); ok {
		fmt.Println(s.String())
	}

	// Tipe yang tidak cocok cukup menghasilkan ok = false.
	if teks, ok := nilai.(string); ok {
		fmt.Println("teks:", teks)
	} else {
		fmt.Println("nilai bukan string, lanjut tanpa panic")
	}
}

Output yang Dihasilkan:

text
ok: true sensor: Ruang Server: 21.5 derajat
suhu ruangan: 21.5
Ruang Server: 21.5 derajat
nilai bukan string, lanjut tanpa panic

Mari kita telaah baris demi baris program di atas:

  1. Baris 6–8 (type Stringer interface): interface buatan sendiri dengan satu method; menjadi sasaran assertion ke interface di baris 34.
  2. Baris 15–17 (func (s Sensor) String): method value receiver ini membuat Sensor sekaligus memenuhi Stringer — dua kontrak dengan satu method.
  3. Baris 20 (var nilai any = Sensor{...}): struct dibungkus ke dalam any; sejak baris ini dynamic type-nya main.Sensor.
  4. Baris 23–24 (sensor, ok := nilai.(Sensor)): assertion comma-ok berhasil; ok bernilai true dan sensor bertipe Sensor penuh, bukan lagi any.
  5. Baris 27–31 (if s, ok := ...; ok): variabel s dan ok hanya hidup di dalam blok if, pola yang menjaga scope tetap sempit.
  6. Baris 34–36 (nilai.(Stringer)): assertion ke interface berhasil karena dynamic type Sensor mengimplementasikan String(); hasilnya dipanggil sebagai s.String().
  7. Baris 39–43 (nilai.(string)): tipe tidak cocok, ok bernilai false, cabang else jalan, dan program tetap hidup — inilah perbedaan nyata dengan error-assertion.go.

Type assertion memeriksa satu tipe pada satu waktu. Begitu cabang per tipe bertambah banyak, menumpuk if bertingkat menjadi tidak praktis — dan di situlah type switch mengambil alih, topik chapter berikutnya.

Referensi #

Berkas Sumber

Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.

assertion-demo.go

Unduh berkas
go
package main

import "fmt"

// Stringer adalah tiruan kecil dari fmt.Stringer.
type Stringer interface {
	String() string
}

type Sensor struct {
	Nama string
	Suhu float64
}

func (s Sensor) String() string {
	return fmt.Sprintf("%s: %.1f derajat", s.Nama, s.Suhu)
}

func main() {
	var nilai any = Sensor{Nama: "Ruang Server", Suhu: 21.5}

	// Bentuk comma-ok: aman, ok bernilai false tanpa panic.
	sensor, ok := nilai.(Sensor)
	fmt.Println("ok:", ok, "sensor:", sensor)

	// Kombinasi comma-ok dalam if untuk cabang yang hanya dipakai bila cocok.
	if s, ok := nilai.(Sensor); ok {
		fmt.Println("suhu ruangan:", s.Suhu)
	} else {
		fmt.Println("nilai bukan Sensor")
	}

	// Type assertion ke interface: apakah tipe dinamis mengimplementasikan Stringer?
	if s, ok := nilai.(Stringer); ok {
		fmt.Println(s.String())
	}

	// Tipe yang tidak cocok cukup menghasilkan ok = false.
	if teks, ok := nilai.(string); ok {
		fmt.Println("teks:", teks)
	} else {
		fmt.Println("nilai bukan string, lanjut tanpa panic")
	}
}

error-assertion.go

Unduh berkas
go
package main

import "fmt"

func main() {
	var nilai any = "bukan angka"

	// Bentuk satu nilai: tipe tidak cocok menyebabkan panic saat runtime.
	angka := nilai.(int)
	fmt.Println("baris ini tidak pernah tercetak:", angka)
}