Chapter sebelumnya menutup dengan satu masalah yang belum selesai: break hanya mampu keluar dari perulangan yang paling dalam. Pencarian pada data dua dimensi, validasi bertingkat, dan pemrosesan matriks sering membutuhkan lompatan yang menembus beberapa tingkat loop sekaligus. Go menjawabnya dengan label: nama yang kita tempelkan pada perulangan luar, lalu dituju oleh break atau continue. Bahasa ini juga masih menyediakan goto warisan, tetapi perannya sangat terbatas. Pada chapter ini kita memakai label dengan break dan continue, lalu mempelajari batasan ketat goto beserta satu kesalahan kompilasi yang lazim menghantamnya.

break dengan Label #

Label dideklarasikan dengan menulis nama diikuti tanda titik dua di baris sebelum perulangan. Nama label harus berbeda dari nama label lain dalam fungsi yang sama, dan menurut kebiasaan ditulis dengan huruf kecil. Perhatikan penempatannya pada contoh berikut:

go
cari:
	for i, baris := range tabel {
		for j, nilai := range baris {
			if nilai == target {
				break cari
			}
		}
	}

Ketika break cari dieksekusi, alur keluar dari kedua perulangan sekaligus — loop luar yang berlabel cari dan loop dalam di dalamnya — lalu mendarat pada statement setelah loop luar. Tanpa label, break hanya menghentikan loop dalam seperti yang kita buktikan di chapter break dan continue .

continue dengan Label #

Label juga dapat dituju continue. Lompatannya berbeda: alur kembali ke titik lanjutan iterasi perulangan yang berlabel — melewati sisa loop dalam dan sisa badan loop luar untuk iterasi saat ini. Pola ini berguna untuk melewati satu kelompok data yang tidak lengkap tanpa menghentikan pemrosesan kelompok lain.

Program Utuh: Pencarian Berlabel dan goto Minimal #

Program berikut mencari angka target di dalam tabel dua dimensi dengan break cari, melewati baris peserta yang memuat nama kosong dengan continue barisLuar, lalu memakai goto untuk melompat ke akhir fungsi. Berkas label-demo.go pada bagian bawah halaman memuat program yang sama:

go
package main

import "fmt"

func main() {
	tabel := [][]int{
		{7, 2, 9},
		{4, 5, 6},
		{1, 8, 3},
	}
	target := 8

cari:
	for i, baris := range tabel {
		for j, nilai := range baris {
			if nilai == target {
				fmt.Printf("Target %d ditemukan di baris %d, kolom %d\n", target, i, j)
				break cari
			}
		}
	}

	peserta := [][]string{
		{"Budi", "Siti", ""},
		{"Andi", "Rina"},
	}

barisLuar:
	for _, daftar := range peserta {
		for _, nama := range daftar {
			if nama == "" {
				fmt.Println("Ada nama kosong, seluruh baris dilewati")
				continue barisLuar
			}
			fmt.Println("Peserta valid:", nama)
		}
	}

	fmt.Println("Menyiapkan laporan")
	goto selesai

	fmt.Println("Baris ini tidak pernah tercetak")

selesai:
	fmt.Println("Laporan selesai dibuat")
}

Jalankan program dari direktori chapter dengan perintah berikut:

bash
go run label-demo.go

Output yang Dihasilkan:

text
Target 8 ditemukan di baris 2, kolom 1
Peserta valid: Budi
Peserta valid: Siti
Ada nama kosong, seluruh baris dilewati
Peserta valid: Andi
Peserta valid: Rina
Menyiapkan laporan
Laporan selesai dibuat

Mari kita telaah baris demi baris program di atas:

  1. Baris 12 (cari:): mendeklarasikan label bernama cari untuk perulangan pencarian. Label tidak dieksekusi; label hanya penanda tujuan lompatan.
  2. Baris 13-21 (perulangan bersarang pencarian): loop luar mengambil tiap baris tabel, loop dalam memeriksa tiap sel. Ketika sel bernilai 8, posisinya dicetak lalu break cari mengakhiri kedua loop sekaligus — dua baris terakhir tabel tidak pernah diperiksa.
  3. Baris 30 (barisLuar:): label untuk perulangan pemrosesan peserta.
  4. Baris 31-41 (perulangan bersarang peserta): loop dalam mencetak nama yang valid. Saat bertemu string kosong pada baris pertama, continue barisLuar melewati sisa baris tersebut — nama setelah sel kosong diabaikan — lalu iterasi berlanjut ke baris kedua yang diproses penuh.
  5. Baris 45 (goto selesai): melompat langsung ke label selesai di baris 49. Statement fmt.Println di baris 47 menjadi kode tak terjangkau dan tidak pernah tereksekusi.
  6. Baris 49-50 (selesai: dan cetak): titik mendarat goto. Program menyelesaikan pekerjaannya dengan satu pesan penutup.

goto dan Batasan Ketatnya #

goto mentransfer alur ke label pada fungsi yang sama tanpa syarat. Bentuknya sederhana — goto namaLabel dan namaLabel: — tetapi kompilator memasang pagar yang ketat: goto tidak boleh melompati deklarasi variabel yang berada di antara lompatan dan labelnya. Berkas error-goto.go pada folder chapter ini memuat pelanggarannya:

go
package main

import "fmt"

func main() {
	goto selesai

	pesan := "baris ini dilompati"

selesai:
	fmt.Println(pesan)
}

Jalankan berkas tersebut dan amati penolakan kompilator:

bash
go run error-goto.go

Output yang Dihasilkan:

text
# command-line-arguments
./error-goto.go:6:7: goto selesai jumps over declaration of pesan at ./error-goto.go:8:8

Pesan tersebut menjelaskan dua lokasi sekaligus: goto di baris 6 kolom 7, dan deklarasi pesan di baris 8 yang dilompati. Alasannya fundamental — variabel pesan hanya hidup setelah deklarasinya dieksekusi, sehingga mendarat di label selesai membuat fmt.Println(pesan) membaca variabel yang belum pernah terbentuk.

Dengan label, kendali lompatan Go sudah lengkap. Satu topik terakhir di Section ini tidak berhubungan dengan lompatan, melainkan dengan penjadwalan: defer, mekanisme Go untuk memastikan pekerjaan pembersihan selalu berjalan saat fungsi selesai — topik chapter penutup Section Kendali Alur.

Referensi #

Berkas Sumber

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

error-goto.go

Unduh berkas
go
package main

import "fmt"

func main() {
	goto selesai

	pesan := "baris ini dilompati"

selesai:
	fmt.Println(pesan)
}

label-demo.go

Unduh berkas
go
package main

import "fmt"

func main() {
	tabel := [][]int{
		{7, 2, 9},
		{4, 5, 6},
		{1, 8, 3},
	}
	target := 8

cari:
	for i, baris := range tabel {
		for j, nilai := range baris {
			if nilai == target {
				fmt.Printf("Target %d ditemukan di baris %d, kolom %d\n", target, i, j)
				break cari
			}
		}
	}

	peserta := [][]string{
		{"Budi", "Siti", ""},
		{"Andi", "Rina"},
	}

barisLuar:
	for _, daftar := range peserta {
		for _, nama := range daftar {
			if nama == "" {
				fmt.Println("Ada nama kosong, seluruh baris dilewati")
				continue barisLuar
			}
			fmt.Println("Peserta valid:", nama)
		}
	}

	fmt.Println("Menyiapkan laporan")
	goto selesai

	fmt.Println("Baris ini tidak pernah tercetak")

selesai:
	fmt.Println("Laporan selesai dibuat")
}