Alur Kontrol

if sebagai ekspresi, loop, while, dan for dengan range.

Daftar isi

Topik ini membahas cara mengarahkan jalannya program Rust: percabangan dengan if yang sekaligus berfungsi sebagai ekspresi, perulangan dengan loop, while, dan for, serta pemakaian rentang pada perulangan for. Setelah membacanya, pembaca dapat memilih bentuk alur kontrol yang paling aman untuk setiap kebutuhan dan memahami syarat tipe yang menyertainya.

Percabangan dengan if#

Percabangan memilih blok kode yang dijalankan berdasarkan sebuah kondisi. Kata kunci if diikuti kondisi tanpa tanda kurung, lalu blok kodenya dibatasi kurung kurawal, dan titik koma tidak ditulis setelah kurung kurawal penutup.

rust genap.rs
fn main() {
    let angka = 7;

    if angka % 2 == 0 {
        println!("{angka} adalah bilangan genap");
    } else {
        println!("{angka} adalah bilangan ganjil");
    }
}
text Keluaran
7 adalah bilangan ganjil

Kondisi wajib bertipe bool karena Rust tidak menganggap bilangan maupun nilai lain sebagai benar atau salah secara implisit. Tanda kurung di sekitar kondisi tidak diperlukan, dan pemakaiannya justru memicu peringatan unnecessary parentheses dari compiler. Blok else dijalankan ketika kondisi salah. Rantai banyak kondisi disusun dengan else if, dan pemeriksaannya berhenti pada kondisi pertama yang terpenuhi.

rust predikat.rs
fn main() {
    let nilai = 78;

    if nilai >= 90 {
        println!("predikat A");
    } else if nilai >= 80 {
        println!("predikat B");
    } else if nilai >= 70 {
        println!("predikat C");
    } else {
        println!("predikat D");
    }
}
text Keluaran
predikat C

Nilai 78 gagal pada dua kondisi pertama dan berhenti di kondisi ketiga, sehingga hanya satu blok yang dijalankan. Setiap cabang else if hanya diperiksa apabila semua kondisi sebelumnya bernilai salah, sehingga urutan penulisan menentukan hasil. Ketika rantai kondisi memeriksa satu nilai terhadap banyak pola, pencocokan dengan match yang dibahas pada modul struct dan enum menjadi alternatif yang lebih ringkas.

if sebagai ekspresi#

Blok if di Rust adalah ekspresi, sehingga hasilnya dapat langsung diikat ke sebuah variabel. Syaratnya satu, yaitu kedua cabang harus menghasilkan tipe yang sama.

rust if-ekspresi.rs
fn main() {
    let kondisi = true;
    let angka = if kondisi { 10 } else { 20 };

    println!("nilai angka: {angka}");
}
text Keluaran
nilai angka: 10

Pola ini menggantikan kebiasaan dari bahasa lain yang mendeklarasikan variabel kosong terlebih dahulu, lalu mengisinya di setiap cabang. Nilai if ditentukan oleh ekspresi terakhir tiap blok tanpa titik koma, sama seperti nilai blok pada umumnya. Ketika salah satu cabang bernilai 10 dan cabang lainnya bernilai teks, compiler menolak kedua tipe yang berbeda tersebut.

Cabang else wajib ada ketika nilai if dipakai. Tanpa else, nilai keseluruhan ekspresi pada saat kondisi salah adalah tipe unit (), sehingga penulisan let angka = if kondisi { 10 }; ditolak compiler dengan error E0317. Ketika if hanya menjalankan blok dan hasilnya tidak diambil sebagai nilai, cabang else boleh ditiadakan.

Perulangan dengan loop#

Kata kunci loop menjalankan blok kode tanpa henti sampai bertemu perintah break. Perintah break juga dapat membawa nilai yang menjadi hasil seluruh ekspresi loop.

rust hitung_loop.rs
fn main() {
    let mut hitung = 0;

    let hasil = loop {
        hitung += 1;
        if hitung == 10 {
            break hitung * 2;
        }
    };

    println!("hasil loop: {hasil}");
}
text Keluaran
hasil loop: 20

Perulangan berhenti ketika hitung mencapai 10, dan nilai break berupa 20 menjadi nilai variabel hasil. Bentuk ini berguna untuk perulangan pencarian yang mengembalikan sesuatu begitu ditemukan. Ketika perulangan bersarang, label seperti 'luar: dapat dipasang pada loop terluar supaya break 'luar menghentikan seluruh perulangan sekaligus, bukan hanya perulangan terdalam tempat perintah ditulis.

Kata kunci continue melompati sisa blok dan langsung memulai putaran berikutnya. Perintah ini berlaku pada semua bentuk perulangan. Tanpa break, loop berjalan tanpa akhir, sehingga pola yang mengulang sampai sebuah operasi berhasil merupakan kandidat alami untuk loop.

Perulangan dengan while#

Perulangan while memeriksa kondisi sebelum setiap putaran dan berhenti ketika kondisi menjadi salah. Bentuk ini cocok ketika jumlah putaran tidak diketahui di depan, misalnya memproses antrean yang isinya berkurang. Kondisi diperiksa juga sebelum putaran pertama, sehingga blok perulangan tidak dijalankan sama sekali ketika kondisi sudah bernilai salah sejak awal.

rust antrean.rs
fn main() {
    let mut sisa = 3;

    while sisa > 0 {
        println!("sisa antrean: {sisa}");
        sisa -= 1;
    }

    println!("antrean selesai");
}
text Keluaran
sisa antrean: 3
sisa antrean: 2
sisa antrean: 1
antrean selesai

Kondisi sisa > 0 bernilai benar pada tiga putaran pertama, lalu salah setelah sisa mencapai nol, dan eksekusi lanjut ke baris setelah kurung kurawal penutup. Perintah break dan continue juga berlaku pada while dengan perilaku yang sama seperti pada loop. Perhatikan bahwa pengubahan nilai sisa di dalam blok merupakan bagian penting dari perulangan yang benar, sebab tanpa perubahan tersebut kondisi tidak pernah menjadi salah dan perulangan tidak berhenti. Alur pengambilan keputusan berulang ini tampak pada diagram berikut.

sisa = 3sisa > 0 ?yacetak sisasisa -= 1ulangitidakantrean selesai
Alur perulangan while pada program antrean

Perulangan dengan for dan rentang#

Perulangan for menjalankan blok kode untuk setiap nilai yang dihasilkan sebuah iterator, yakni sumber nilai yang mengeluarkan isinya satu per satu. Bentuk ini menjadi pilihan utama di Rust karena aman dari kesalahan indeks. Sumber nilai yang paling lazim adalah rentang (range).

rust rentang.rs
fn main() {
    for angka in 1..4 {
        println!("angka: {angka}");
    }

    for detik in (1..=3).rev() {
        println!("hitung mundur: {detik}");
    }

    let nilai = [80, 90, 100];
    for n in nilai {
        println!("nilai: {n}");
    }
}
text Keluaran
angka: 1
angka: 2
angka: 3
hitung mundur: 3
hitung mundur: 2
hitung mundur: 1
nilai: 80
nilai: 90
nilai: 100

Rentang 1..4 menghasilkan nilai 1, 2, dan 3 tanpa angka 4, sedangkan 1..=3 memuat angka 3 di ujungnya. Metode .rev() membalik urutan sehingga cocok untuk hitung mundur. Array juga dapat diiterasi langsung elemen demi elemen tanpa indeks, dan tiap elemen tersedia sebagai variabel n di dalam blok. Ketika jumlah putaran sudah diketahui atau sumber nilainya berupa koleksi, bentuk for umumnya menjadi pilihan yang tepat, dan panduan singkatnya tersedia pada tabel berikut.

BentukKapan dipakai
loopperulangan tanpa batas sampai break, termasuk pencarian yang mengembalikan nilai
whilejumlah putaran tidak diketahui dan bergantung pada kondisi yang berubah
formengiterasi rentang, array, atau koleksi dengan jumlah elemen yang jelas

Bentuk for juga bekerja sama dengan iterator yang dibahas pada modul iterator dan closure. Rentang dan array pada contoh di atas hanyalah dua contoh sumber nilai untuk for, karena banyak jenis koleksi lain dapat diiterasi dengan cara yang sama. Pola yang diperkenalkan pada topik ini tetap dipakai pada kode tingkat lanjut.

Kesalahan umum#

Memakai bilangan sebagai kondisi. Kebiasaan dari bahasa yang menganggap bilangan nol sebagai salah langsung ditolak compiler, seperti contoh berikut.

bash
printf 'fn main() {\n    let angka = 3;\n    if angka {\n        println!("tiga");\n    }\n}\n' > salah.rs
rustc --edition=2021 salah.rs 2>&1 | sed -n '1,7p'
text Keluaran
error[E0308]: mismatched types
 --> salah.rs:3:8
  |
3 |     if angka {
  |        ^^^^^ expected `bool`, found integer

error: aborting due to 1 previous error

Perbaikannya adalah menuliskan perbandingan yang jelas, misalnya if angka != 0 ketika maksudnya memeriksa bilangan tidak nol. Error yang sama muncul pada kondisi while karena kedua konstruksi menuntut tipe bool.

Mencampur tipe cabang pada if sebagai ekspresi. Penulisan if kondisi { 10 } else { "sepuluh" } gagal dikompilasi karena satu cabang bernilai bilangan dan cabang lainnya bernilai teks, dan error yang muncul adalah E0308 seperti pada kasus tipe yang tidak cocok lainnya. Ubah salah satu cabang atau konversikan nilainya sehingga keduanya bertipe sama. Pemakaian seperti ini biasanya menandakan kedua cabang sebenarnya menghasilkan hal yang berbeda sehingga lebih tepat dipecah menjadi percabangan biasa.

Ringkasan#

  • Kondisi if dan while wajib bertipe bool tanpa konversi implisit dari bilangan maupun nilai lain.
  • if adalah ekspresi yang hasilnya dapat diikat ke variabel asalkan semua cabang bertipe sama.
  • loop berjalan tanpa henti sampai break, dan break dapat membawa nilai hasil ekspresi loop.
  • while memeriksa kondisi sebelum setiap putaran, sedangkan label mengatur break pada perulangan bersarang.
  • for mengiterasi rentang a..b, a..=b, maupun koleksi langsung, dan menjadi bentuk perulangan paling aman.

Referensi#