Perulangan bersarang memperkenalkan masalah yang tidak ada pada perulangan tunggal: break dan continue hanya mengenai perulangan terdalam yang membungkusnya. Bayangkan pencarian di tabel dua dimensi — begitu nilai yang dicari ditemukan, tidak ada gunanya memeriksa baris dan kolom sisanya, tetapi break biasa hanya mampu keluar dari perulangan kolom; perulangan baris tetap berjalan dan memulai pemindaian baris baru. Rust menyelesaikan masalah ini dengan label (label loop): nama yang kita berikan pada sebuah perulangan sehingga break dan continue dapat menunjuk perulangan mana yang ingin dikendalikan secara persis.
Masalah break pada Perulangan Bersarang #
Mari kita perhatikan dulu perilaku bawaan break di dalam perulangan bersarang. Program berikut menghentikan perulangan dalam ketika column mencapai 2, dan output-nya memperlihatkan akibatnya terhadap perulangan luar:
fn main() {
for row in 1..=2 {
for column in 1..=3 {
if column == 2 {
break;
}
println!("Baris {}, kolom {}", row, column);
}
}
}Output yang Dihasilkan:
Baris 1, kolom 1
Baris 2, kolom 1Mari kita telaah alur eksekusi di atas:
- Baris 2 (
for row in 1..=2 {): perulangan luar dimulai denganrow = 1. - Baris 3 (
for column in 1..=3 {): perulangan dalam berjalan dengancolumn = 1. - Baris 4 (
if column == 2 {): pemeriksaan bernilaifalseuntukcolumn = 1, sehingga baris cetak berjalan dan menghasilkanBaris 1, kolom 1. - Baris 5 (
break;): padacolumn = 2,breakmenghentikan perulangan dalam — hanya perulangan kolom yang berhenti, bukan perulangan baris. - Perulangan luar melanjutkan pekerjaannya:
row = 2memulai perulangan dalam yang baru, danbreakyang sama terulang padacolumn = 2setelah mencetakBaris 2, kolom 1.
Kedua kali break dieksekusi, perulangan luar tidak tersentuh sama sekali. Perilaku “hanya perulangan terdalam” tersebut adalah aturan tetap Rust, dan pada banyak kasus justru menjadi batasan — pencarian dua dimensi yang sudah menemukan hasilnya tetap dipaksa memindai sisa baris.
Memberi Label pada Perulangan #
Label didefinisikan dengan menuliskan nama berawalan tanda kutip tunggal diikuti titik dua sebelum kata kunci perulangan, misalnya 'outer: for .... Nama label harus unik pada fungsi tersebut, dan kebiasaan penamaannya pendek serta deskriptif. Setelah label terpasang, break 'nama; menghentikan perulangan pemilik label tersebut — berapa pun tingkat perulangan yang memisahkan statement break dari labelnya. Mari kita terapkan label 'outer pada program sebelumnya:
fn main() {
'outer: for row in 1..=2 {
for column in 1..=3 {
if column == 2 {
break 'outer;
}
println!("Baris {}, kolom {}", row, column);
}
}
println!("Perulangan luar dihentikan.");
}Output yang Dihasilkan:
Baris 1, kolom 1
Perulangan luar dihentikan.Mari kita bedah perubahan yang terjadi:
- Baris 2 (
'outer: for row in 1..=2 {): label'outermenjadi nama resmi perulangan baris; kehadiran label tidak mengubah cara perulangan berjalan sebelum ada statement yang menunjuknya. - Baris 5 (
break 'outer;): alih-alih menghentikan perulangan kolom,breakbernama ini menembus keluar hingga perulangan'outerdan menghentikan kedua perulangan sekaligus. - Baris 10 (
println!("Perulangan luar dihentikan.");): statement setelah perulangan luar tercapai setelah satu-satunya baris cetak;row = 2tidak pernah diproses dan hanya satu pasangan baris-kolom yang tercetak.
Pencarian Dua Dimensi dengan Label #
Kasus penggunaan label yang paling alami adalah pencarian pada struktur bersarang. Program berikut mencari nilai 45 pada rak gudang dua dimensi yang dimodelkan sebagai array dari array. Begitu nilai ditemukan, seluruh pemindaian dihentikan dari posisi terdalam:
fn main() {
let warehouse = [
[10, 25, 30],
[45, 5, 60],
[70, 80, 90],
];
let mut found = 0;
'grid: for row in warehouse {
for value in row {
if value == 45 {
found = value;
break 'grid;
}
}
}
println!("Nilai {} ditemukan di gudang.", found);
}Output yang Dihasilkan:
Nilai 45 ditemukan di gudang.Mari kita uraikan cara kerja program di atas:
- Baris 2–6 (
let warehouse = [...]): array dua dimensi berupa array luar yang memuat tiga array dalam; setiap array dalam merepresentasikan satu rak dengan tiga nilai stok. - Baris 8 (
let mut found = 0;): variabel penampung hasil pencarian diisi nilai awal0sebelum pencarian dimulai. - Baris 10 (
'grid: for row in warehouse {): label'griddipasang pada perulangan rak; setiap iterasi mengambil satu array rak dariwarehouse. - Baris 11 (
for value in row {): perulangan dalam memeriksa setiap nilai pada rak saat ini —10,25,30, lalu45. - Baris 12 (
if value == 45 {): kecocokan ditemukan pada elemen keempat yang diperiksa, yaitu nilai pertama rak kedua. - Baris 14 (
break 'grid;):breakbernama menghentikan seluruh pemindaian rak; nilai5,60, dan seluruh rak ketiga tidak pernah dibaca. - Baris 19 (
println!("Nilai {} ditemukan di gudang.", found);): hasil pencarian dicetak setelah kedua perulangan berakhir melalui satubreak 'grid.
Tanpa label, kita memerlukan variabel penanda tambahan — misalnya found yang juga diperiksa oleh kondisi perulangan luar — untuk menghentikan pemindaian dua tingkat. Label memampatkan logika penghentian tersebut menjadi satu statement yang jelas menunjuk targetnya.
continue dengan Label #
continue juga dapat menunjuk label, dengan makna yang berbeda dari break 'label: continue 'label; memulai iterasi berikutnya dari perulangan yang berlabel, sehingga seluruh perulangan yang lebih dalam selesai ditinggalkan lebih awal. Program berikut melewati seluruh meja nomor 2 yang sedang dibersihkan tanpa memproses satu kursi pun dari meja tersebut:
fn main() {
'meja: for table in 1..=3 {
if table == 2 {
println!("Meja {} sedang dibersihkan, lewati.", table);
continue 'meja;
}
for seat in 1..=2 {
println!("Meja {} kursi {} terisi.", table, seat);
}
}
}Output yang Dihasilkan:
Meja 1 kursi 1 terisi.
Meja 1 kursi 2 terisi.
Meja 2 sedang dibersihkan, lewati.
Meja 3 kursi 1 terisi.
Meja 3 kursi 2 terisi.Mari kita bedah program di atas:
- Baris 2 (
'meja: for table in 1..=3 {): label'mejadipasang pada perulangan meja yang berjalan dari meja 1 sampai 3. - Baris 3 (
if table == 2 {): pemeriksaan penjaga menyaring meja yang sedang dibersihkan sebelum perulangan kursi dimulai. - Baris 5 (
continue 'meja;): statement ini melompat ke iterasi meja berikutnya (table = 3) — perulangan kursi di bawahnya tidak pernah dimasuki untuk meja 2. - Baris 8 (
for seat in 1..=2 {): perulangan kursi hanya berjalan untuk meja 1 dan meja 3, masing-masing mencetak dua kursi terisi.
Pada contoh ini continue 'meja berada di badan perulangan berlabel itu sendiri, jadi pengaruhnya sama dengan continue; biasa. Nilai sebenarnya dari continue 'label muncul ketika statement tersebut ditulis jauh di dalam perulangan bersarang — kita akan melewatkan seluruh tingkatan perulangan dalam sekaligus tanpa memerlukan penanda tambahan.
Ringkasan #
Label memberi nama pada perulangan sehingga break 'nama; dapat menghentikan perulangan yang ditunjuk dari posisi berapa pun di dalam perulangan bersarang, dan continue 'nama; dapat melompat ke iterasi berikutnya perulangan tersebut. Tanpa label, kedua kata kunci hanya mengendalikan perulangan terdalam — perilaku yang sering tidak memadai untuk pencarian dua dimensi atau pemrosesan tabel bersarang. Dengan bekal pengendalian perulangan yang kini lengkap, kita berpindah ke topik pencocokan nilai dengan match.