Control Flow
Kemampuan buat ngejalanin kode tergantung dari apakah sebuah kondisi itu true
dan buat ngejalanin kode berulang kali pas sebuah kondisi itu true adalah blok
dasar di kebanyakan bahasa pemrograman. Konstruk paling umum yang ngebolehin
kita ngatur alur eksekusi kode Rust adalah ekspresi if sama loop.
Ekspresi if
Ekspresi if ngebolehin kita buat nyabangin kode tergantung kondisinya. Kita
ngasih sebuah kondisi terus bilang, “Kalau kondisi ini terpenuhi, jalanin blok
kode ini. Kalau nggak terpenuhi, jangan jalanin blok kode ini.”
Bikin project baru namanya branches di direktori projects kita buat eksplor
ekspresi if. Di file src/main.rs, masukin kode berikut:
Nama file: src/main.rs
fn main() {
let number = 3;
if number < 5 {
println!("condition was true");
} else {
println!("condition was false");
}
}
Semua ekspresi if dimulai pake keyword if, diikuti sama sebuah kondisi. Di
kasus ini, kondisinya nge-cek apakah variabel number punya nilai kurang dari 5.
Kita taruh blok kode yang bakal jalan kalau kondisinya true tepat setelah
kondisinya di dalem kurung kurawal. Blok kode yang terkait sama kondisi di
ekspresi if kadang disebut arms (lengan), sama kayak arms di ekspresi
match yang kita bahas di bagian “Membandingkan Tebakan dengan Secret Number”
di Bab 2.
Opsionalnya, kita juga bisa masukin ekspresi else, kayak yang kita lakuin di
sini, buat ngasih program blok kode alternatif buat jalan kalau kondisinya
ternyata false. Kalau kita nggak ngasih ekspresi else dan kondisinya
false, programnya bakal langsung lewatin blok if terus lanjut ke kode
selanjutnya.
Coba jalanin kode ini; kita bakal dapet output kayak gini:
$ cargo run
Compiling branches v0.1.0 (file:///projects/branches)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.31s
Running `target/debug/branches`
condition was true
Yuk coba ubah nilai number jadi nilai yang bikin kondisinya false buat liat
apa yang terjadi:
fn main() {
let number = 7;
if number < 5 {
println!("condition was true");
} else {
println!("condition was false");
}
}
Jalanin programnya lagi, terus liat output-nya:
$ cargo run
Compiling branches v0.1.0 (file:///projects/branches)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.31s
Running `target/debug/branches`
condition was false
Penting juga buat dicatet kalau kondisi di kode ini harus sebuah bool. Kalau
kondisinya bukan bool, kita bakal dapet error. Contohnya, coba jalanin kode
berikut:
Nama file: src/main.rs
fn main() {
let number = 3;
if number {
println!("number was three");
}
}
Kondisi if dievaluasi jadi nilai 3 kali ini, dan Rust ngelepar error:
$ cargo run
Compiling branches v0.1.0 (file:///projects/branches)
error[E0308]: mismatched types
--> src/main.rs:4:8
|
4 | if number {
| ^^^^^^ expected `bool`, found integer
For more information about this error, try `rustc --explain E0308`.
error: could not compile `branches` (bin "branches") due to 1 previous error
Error-nya nunjukin kalau Rust ngarepin bool tapi dapetnya integer. Beda sama
bahasa kayak Ruby sama JavaScript, Rust nggak bakal otomatis nyoba convert tipe
non-Boolean jadi Boolean. Kita harus eksplisit dan selalu ngasih if sebuah
Boolean sebagai kondisinya. Kalau kita mau blok kode if jalan cuma pas sebuah
angka nggak sama dengan 0, misalnya, kita bisa ubah ekspresi if-nya jadi
kayak gini:
Nama file: src/main.rs
fn main() {
let number = 3;
if number != 0 {
println!("number was something other than zero");
}
}
Jalanin kode ini bakal nyetak number was something other than zero.
Handle Banyak Kondisi dengan else if
Kita bisa pake banyak kondisi dengan ngelempokin if sama else di sebuah
ekspresi else if. Contohnya:
Nama file: src/main.rs
fn main() {
let number = 6;
if number % 4 == 0 {
println!("number is divisible by 4");
} else if number % 3 == 0 {
println!("number is divisible by 3");
} else if number % 2 == 0 {
println!("number is divisible by 2");
} else {
println!("number is not divisible by 4, 3, or 2");
}
}
Program ini punya empat kemungkinan jalur yang bisa diambil. Setelah jalanin programnya, kita bakal liat output kayak gini:
$ cargo run
Compiling branches v0.1.0 (file:///projects/branches)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.31s
Running `target/debug/branches`
number is divisible by 3
Pas program ini jalan, dia cek tiap ekspresi if secara berurutan terus
ngejalanin body pertama yang kondisinya dievaluasi jadi true. Inget ya
walaupun 6 itu bisa dibagi 2, kita nggak liat output number is divisible by 2,
dan kita juga nggak liat teks number is not divisible by 4, 3, or 2 dari blok
else. Itu karena Rust cuma ngejalanin blok buat kondisi true yang pertama,
dan sekali dia nemu satu, dia bahkan nggak bakal cek sisanya.
Pake terlalu banyak ekspresi else if bisa bikin kode kita berantakan, jadi
kalau kita punya lebih dari satu, mendingan di-refactor kodenya. Bab 6 jelasin
konstruk percabangan Rust yang sangat kuat namanya match buat kasus-kasus
kayak gini.
Pake if di Statement let
Karena if itu adalah sebuah ekspresi, kita bisa pakenya di sisi kanan
statement let buat ngasih hasil kondisinya ke sebuah variabel, kayak di
Listing 3-2.
fn main() {
let condition = true;
let number = if condition { 5 } else { 6 };
println!("The value of number is: {number}");
}
if ke sebuah variabelVariabel number bakal di-bind ke sebuah nilai berdasarkan hasil dari ekspresi
if. Jalanin kode ini buat liat apa yang terjadi:
$ cargo run
Compiling branches v0.1.0 (file:///projects/branches)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.30s
Running `target/debug/branches`
The value of number is: 5
Inget ya kalau blok kode dievaluasi jadi ekspresi terakhir di dalemnya, dan
angka sendirian itu juga sebuah ekspresi. Di kasus ini, nilai dari seluruh
ekspresi if tergantung dari blok kode mana yang jalan. Ini artinya nilai yang
berpotensi jadi hasil dari tiap arm di if harus punya tipe yang sama; di
Listing 3-2, hasil dari baik arm if maupun arm else adalah integer
i32. Kalau tipenya nggak cocok, kayak di contoh berikut, kita bakal dapet
error:
Nama file: src/main.rs
fn main() {
let condition = true;
let number = if condition { 5 } else { "six" };
println!("The value of number is: {number}");
}
Pas kita nyoba compile kode ini, kita bakal dapet error. Arm if sama else
punya tipe nilai yang nggak kompatibel, dan Rust nunjukin tepat di mana letak
masalahnya di program kita:
$ cargo run
Compiling branches v0.1.0 (file:///projects/branches)
error[E0308]: `if` and `else` have incompatible types
--> src/main.rs:4:44
|
4 | let number = if condition { 5 } else { "six" };
| - ^^^^^ expected integer, found `&str`
| |
| expected because of this
For more information about this error, try `rustc --explain E0308`.
error: could not compile `branches` (bin "branches") due to 1 previous error
Ekspresi di blok if dievaluasi jadi integer, dan ekspresi di blok else
dievaluasi jadi string. Ini nggak bakal bisa karena variabel harus punya tipe
tunggal, dan Rust perlu tau pas compile time tipe apa variabel number itu,
secara definitif. Tau tipe dari number bikin compiler bisa verifikasi kalau
tipenya valid di mana pun kita pake number. Rust nggak bakal bisa ngelakuin
itu kalau tipe number cuma bisa ditentuin pas runtime; compiler-nya bakal
jadi lebih ribet dan bakal ngasih jaminan yang lebih dikit soal kodenya kalau
dia harus jagain banyak tipe hipotetis buat variabel apa pun.
Pengulangan dengan Loops
Sering kali berguna buat ngejalanin sebuah blok kode lebih dari sekali. Buat tugas ini, Rust nyediain beberapa jenis loops, yang bakal ngejalanin kode di dalem body loop sampe selesai terus langsung mulai lagi dari awal. Buat eksperimen sama loops, yuk kita bikin project baru namanya loops.
Rust punya tiga jenis loop: loop, while, dan for. Yuk kita coba satu-satu.
Ngulang Kode pake loop
Keyword loop ngasih tau Rust buat ngejalanin sebuah blok kode terus-menerus
selamanya sampe kita eksplisit nyuruh dia berhenti.
Sebagai contoh, ubah file src/main.rs di direktori loops kita jadi kayak gini:
Nama file: src/main.rs
fn main() {
loop {
println!("again!");
}
}
Pas kita jalanin program ini, kita bakal liat again! dicetak terus-menerus
tanpa henti sampe kita stop programnya secara manual. Kebanyakan terminal
support keyboard shortcut ctrl-c buat interupsi program
yang terjebak di loop terus-menerus. Cobain deh:
$ cargo run
Compiling loops v0.1.0 (file:///projects/loops)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.08s
Running `target/debug/loops`
again!
again!
again!
again!
^Cagain!
Simbol ^C merepresentasikan di mana kita teken ctrl-c.
Kita mungkin liat atau nggak liat kata again! dicetak setelah ^C, tergantung
di mana kodenya lagi ada di dalem loop pas dia nerima sinyal interupsi.
Untungnya, Rust juga nyediain cara buat keluar dari loop pake kode. Kita bisa
naruh keyword break di dalem loop buat ngasih tau program kapan harus berhenti
ngejalanin loop-nya. Inget kan kita udah lakuin ini di game tebak angka di bagian
“Quit Setelah Tebakan Bener” di Bab 2 buat
keluar dari program pas user menangin gamenya dengan nebak angka yang bener.
Kita juga pake continue di game tebak angka, yang di dalem loop ngasih tau
program buat lewatin sisa kode di iterasi loop ini terus lanjut ke iterasi
berikutnya.
Balikin Nilai dari Loops
Salah satu kegunaan loop itu buat nyoba lagi sebuah operasi yang kita tau
mungkin gagal, kayak nge-cek apakah sebuah thread udah kelar tugasnya. Kita
mungkin juga perlu masukin hasil dari operasi itu keluar dari loop ke sisa kode
kita. Buat lakuin ini, kita bisa nambahin nilai yang mau dibalikin setelah
ekspresi break yang kita pake buat stop loop-nya; nilai itu bakal dibalikin
keluar dari loop biar bisa kita pake, kayak yang ditunjukin di sini:
fn main() {
let mut counter = 0;
let result = loop {
counter += 1;
if counter == 10 {
break counter * 2;
}
};
println!("The result is {result}");
}
Sebelum loop, kita mendeklarasikan variabel namanya counter terus
diinisialisasi jadi 0. Terus kita mendeklarasikan variabel namanya result
buat nampung nilai yang dibalikin dari loop. Di tiap iterasi loop-nya, kita
nambahin 1 ke variabel counter, terus cek apakah counter sama dengan
10. Pas udah sama, kita pake keyword break bareng nilai counter * 2.
Setelah loop, kita pake titik koma buat ngakhiri statement yang ngasih nilainya
ke result. Akhirnya, kita nyetak nilai di result, yang di kasus ini
hasilnya 20.
Kita juga bisa return dari dalem loop. Kalau break cuma keluar dari loop
saat ini, return bakal selalu keluar dari fungsi saat ini.
Loop Labels buat Bedain Banyak Loops
Kalau kita punya loop di dalem loop, break sama continue berlaku buat loop
paling dalem di titik itu. Kita opsional bisa nentuin loop label di sebuah
loop yang terus bisa kita pake bareng break atau continue buat nentuin
kalau keyword itu berlaku buat loop yang dikasih label bukannya loop paling
dalem. Loop label harus dimulai pake kutip tunggal. Ini contoh dengan dua loop
bersarang:
fn main() {
let mut count = 0;
'counting_up: loop {
println!("count = {count}");
let mut remaining = 10;
loop {
println!("remaining = {remaining}");
if remaining == 9 {
break;
}
if count == 2 {
break 'counting_up;
}
remaining -= 1;
}
count += 1;
}
println!("End count = {count}");
}
Loop luarnya punya label 'counting_up, dan dia bakal ngitung dari 0 sampe 2.
Loop dalemnya tanpa label ngitung mundur dari 10 sampe 9. break pertama yang
nggak nentuin label cuma bakal keluar dari loop dalem aja. Statement
break 'counting_up; bakal keluar dari loop luar. Kode ini nyetak:
$ cargo run
Compiling loops v0.1.0 (file:///projects/loops)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.58s
Running `target/debug/loops`
count = 0
remaining = 10
remaining = 9
count = 1
remaining = 10
remaining = 9
count = 2
remaining = 10
End count = 2
Conditional Loops dengan while
Sebuah program sering kali perlu nge-evaluasi sebuah kondisi di dalem loop. Pas
kondisinya true, loop-nya jalan. Pas kondisinya udah nggak true lagi,
programnya manggil break, yang stop loop-nya. Mungkin aja buat
mengimplementasikan perilaku kayak gini pake kombinasi loop, if, else,
sama break; kita bisa cobain itu sekarang di sebuah program kalau mau. Tapi,
pola ini saking umumnya sampe Rust punya konstruk bahasa bawaan buat itu,
namanya while loop. Di Listing 3-3, kita pake while buat ngulang programnya
tiga kali, ngitung mundur tiap kalinya, terus setelah loop-nya kelar, nyetak
pesan terus exit.
fn main() {
let mut number = 3;
while number != 0 {
println!("{number}!");
number -= 1;
}
println!("LIFTOFF!!!");
}
while loop buat jalanin kode pas sebuah kondisi dievaluasi jadi true outdoorKonstruk ini ngilangin banyak nesting (sarang) yang bakal diperluin kalau kita
pake loop, if, else, sama break, dan dia lebih jelas. Selama sebuah
kondisi dievaluasi jadi true, kodenya jalan; kalau nggak, dia keluar dari
loop.
Looping Lewat Koleksi dengan for
Kita bisa milih buat pake konstruk while buat looping elemen-elemen dari
sebuah koleksi, kayak array. Contohnya, loop di Listing 3-4 nyetak tiap elemen
di array a.
fn main() {
let a = [10, 20, 30, 40, 50];
let mut index = 0;
while index < 5 {
println!("the value is: {}", a[index]);
index += 1;
}
}
while loopDi sini, kodenya ngitung naik lewat elemen-elemen di array-nya. Dia mulai di
indeks 0, terus looping sampe nyampe indeks terakhir di array-nya (yaitu pas
index < 5 udah nggak true lagi). Jalanin kode ini bakal nyetak tiap elemen
di array:
$ cargo run
Compiling loops v0.1.0 (file:///projects/loops)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.32s
Running `target/debug/loops`
the value is: 10
the value is: 20
the value is: 30
the value is: 40
the value is: 50
Semua lima nilai array muncul di terminal, sesuai ekspektasi. Walaupun index
bakal nyampe nilai 5 di suatu titik, loop-nya berhenti jalan sebelum nyoba
ngambil nilai keenam dari array-nya.
Tapi, pendekatan ini gampang bikin error; kita bisa bikin programnya panic
kalau nilai indeks atau kondisi tes-nya salah. Misalnya, kalau kita ngerubah
definisi array a jadi punya empat elemen tapi lupa update kondisinya jadi
while index < 4, kodenya bakal panic. Dia juga pelan, karena compiler
nambahin kode runtime buat ngelakuin pengecekan kondisional apakah indeksnya
masih di dalem batas array-nya di tiap iterasi loop-nya.
Sebagai alternatif yang lebih singkat, kita bisa pake for loop terus
ngejalanin kode buat tiap item di koleksi. for loop keliatannya kayak kode di
Listing 3-5.
fn main() {
let a = [10, 20, 30, 40, 50];
for element in a {
println!("the value is: {element}");
}
}
for loopPas kita jalanin kode ini, kita bakal liat output yang sama kayak di Listing 3-4.
Yang lebih penting, sekarang kita udah ningkatin keamanan kodenya dan ngilangin
kemungkinan bug yang bisa hasil dari ngelewatin akhir array atau nggak cukup
jauh dan ngelewatin beberapa item. Kode mesin yang dihasilin dari for loops
juga bisa lebih efisien, karena indeksnya nggak perlu dibandingin sama panjang
array-nya di tiap iterasi.
Pake for loop, kita nggak perlu repot-repot ngerubah kode lain kalau kita
ngerubah jumlah nilai di array-nya, beda sama metode yang dipake di Listing 3-4.
Keamanan sama kesingkatan for loops bikin mereka jadi konstruk loop yang
paling sering dipake di Rust. Bahkan di situasi di mana kita mau ngejalanin kode
sejumlah kali tertentu, kayak di contoh hitung mundur yang pake while loop di
Listing 3-3, kebanyakan Rustacean bakal pake for loop. Caranya adalah pake
Range, yang disediain sama standard library, yang nge-generate semua angka
secara berurutan mulai dari satu angka dan berakhir sebelum angka lainnya.
Ini penampakan hitung mundur kalau pake for loop sama metode lain yang belum
kita bahas, rev, buat nge-reverse (balikin) range-nya:
Nama file: src/main.rs
fn main() {
for number in (1..4).rev() {
println!("{number}!");
}
println!("LIFTOFF!!!");
}
Kode ini jauh lebih keren, kan?
Ringkasan
Kita berhasil! Ini bab yang lumayan gede: kita udah belajar soal variabel,
tipe data scalar sama compound, fungsi, komentar, ekspresi if, sama loop!
Buat latihan konsep-konsep yang dibahas di bab ini, coba bikin program buat
ngelakuin hal-hal berikut:
- Convert temperatur antara Fahrenheit sama Celsius.
- Generate angka Fibonacci ke-n.
- Nyetak lirik lagu Natal “The Twelve Days of Christmas,” manfaatin pengulangan yang ada di lagunya.
Pas kita udah siap buat lanjut, kita bakal bahas konsep di Rust yang nggak umum ada di bahasa pemrograman lain: ownership.