Aliran Kontrol
Kemampuan untuk menjalankan beberapa kode tergantung pada apakah suatu kondisi true
dan untuk menjalankan beberapa kode berulang kali saat kondisi adalah true
blok bangunan dasar di sebagian besar bahasa pemrograman. Konstruksi paling umum yang memungkinkan Anda mengontrol aliran eksekusi kode Rust adalah ekspresi if
dan loop.
Ekspresi if
Ekspresi if
memungkinkan Anda untuk mencabangkan kode Anda tergantung pada kondisi. Anda memberikan kondisi dan kemudian menyatakan, “Jika kondisi ini terpenuhi, jalankan blok kode ini. Jika syaratnya tidak terpenuhi, jangan jalankan blok kode ini.”
Buat proyek baru bernama branches di direktori projects Anda untuk menjelajahi ekspresi if
. Di file src/main.rs, masukkan yang berikut ini:
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 dengan kata kunci if
, diikuti dengan kondisi. Dalam hal ini, kondisi memeriksa apakah variabel number
memiliki nilai kurang dari 5
atau tidak. Kami menempatkan blok kode untuk dieksekusi jika kondisi tepat true
setelah kondisi di dalam kurung kurawal. Blok kode yang terkait dengan kondisi dalam ekspresi terkadang if
disebut lengan, seperti lengan dalam ekspresi match
yang telah kita bahas di bagian “Membandingkan Tebakan dengan Angka Rahasia” di Bab 2.
Opsional, kami juga dapat menyertakan ekspresi else
, yang kami pilih untuk dilakukan di sini, untuk memberikan program blok kode alternatif untuk dieksekusi jika kondisi dievaluasi menjadi false
. Jika Anda tidak memberikan ekspresi else
dan kondisinya adalah false
, program hanya akan melewatkan blok if
dan melanjutkan ke bit kode berikutnya.
$ cargo run
Compiling branches v0.1.0 (file:///projects/branches)
Finished dev [unoptimized + debuginfo] target(s) in 0.31s
Running `target/debug/branches`
condition was true
Mari kita coba mengubah nilai number
menjadi nilai yang membuat kondisi false
untuk melihat apa yang terjadi:
fn main() {
let number = 7;
if number < 5 {
println!("condition was true");
} else {
println!("condition was false");
}
}
Jalankan program lagi, dan lihat hasilnya:
$ cargo run
Compiling branches v0.1.0 (file:///projects/branches)
Finished dev [unoptimized + debuginfo] target(s) in 0.31s
Running `target/debug/branches`
condition was false
Perlu diperhatikan juga bahwa kondisi dalam kode ini harus berupa bool
. Jika kondisinya bukan bool
, kita akan mendapatkan error. Misalnya, coba jalankan kode berikut:
Nama file: src/main.rs
fn main() {
let number = 3;
if number {
println!("number was three");
}
}
Kondisi if
dievaluasi ke nilai 3
saat ini, dan Rust melontarkan kesalahan:
$ 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` due to previous error
Kesalahan menunjukkan bahwa Rust mengharapkan bool
tetapi mendapat bilangan bulat. Tidak seperti bahasa Ruby dan JavaScript, Rust tidak akan secara otomatis mencoba mengubah tipe non-Boolean menjadi Boolean. Anda harus eksplisit dan selalu menyediakan if
dengan Boolean sebagai syaratnya. Jika kita ingin blok kode if
berjalan hanya ketika sebuah angka tidak sama dengan 0
, misalnya, kita dapat mengubah ekspresi if
menjadi berikut:
Nama file: src/main.rs
fn main() { let number = 3; if number != 0 { println!("number was something other than zero"); } }
Menjalankan kode ini akan mencetak number was something other than zero
.
Menangani Berbagai Kondisi dengan else if
Anda dapat menggunakan beberapa kondisi dengan menggabungkan if
dan else
dalam sebuah ekspresi else if
. Misalnya:
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 memiliki empat kemungkinan jalur yang dapat diambil. Setelah menjalankannya, Anda akan melihat output berikut:
$ cargo run
Compiling branches v0.1.0 (file:///projects/branches)
Finished dev [unoptimized + debuginfo] target(s) in 0.31s
Running `target/debug/branches`
number is divisible by 3
Saat program ini dijalankan, program ini memeriksa setiap ekspresi if
secara bergiliran dan mengeksekusi badan pertama yang kondisinya dievaluasi menjadi true
. Perhatikan bahwa meskipun 6 habis dibagi 2, kita tidak melihat output number is divisible by 2
, juga tidak melihat teks number is not divisible by 4, 3, or 2
dari blok else
. Itu karena Rust hanya mengeksekusi blok untuk kondisi true
pertama, dan setelah menemukannya, ia bahkan tidak memeriksa sisanya.
Menggunakan terlalu banyak ekspresi else if
dapat mengacaukan kode Anda, jadi jika Anda memiliki lebih dari satu, Anda mungkin ingin memperbaiki kode Anda. Bab 6 menjelaskan konstruksi percabangan Rust yang kuat yang disebut match
untuk kasus ini.
Menggunakan if
dalam Pernyataan let
Karena if
adalah ekspresi, kita dapat menggunakannya di sisi kanan pernyataan let
untuk menetapkan hasil ke variabel, seperti pada Daftar 3-2.
Nama file: src/main.rs
fn main() { let condition = true; let number = if condition { 5 } else { 6 }; println!("The value of number is: {number}"); }
Variabel number
akan terikat pada nilai sesuai hasil ekspresi if
. Jalankan kode ini untuk melihat apa yang terjadi:
$ cargo run
Compiling branches v0.1.0 (file:///projects/branches)
Finished dev [unoptimized + debuginfo] target(s) in 0.30s
Running `target/debug/branches`
The value of number is: 5
Ingatlah bahwa blok kode mengevaluasi ekspresi terakhir di dalamnya, dan angka itu sendiri juga merupakan ekspresi. Dalam hal ini, nilai seluruh ekspresi if
bergantung pada blok kode mana yang dieksekusi. Artinya nilai-nilai yang berpotensi dihasilkan dari masing-masing lengan if
harus bertipe sama; pada Daftar 3-2, hasil lengan if
dan else
adalah i32
bilangan bulat. Jika tipenya tidak cocok, seperti contoh berikut, kita akan mendapatkan kesalahan:
Nama file: src/main.rs
fn main() {
let condition = true;
let number = if condition { 5 } else { "six" };
println!("The value of number is: {number}");
}
Saat kami mencoba mengkompilasi kode ini, kami akan mendapatkan kesalahan. Lengan if
and else
memiliki tipe nilai yang tidak kompatibel, dan Rust menunjukkan dengan tepat di mana menemukan masalah dalam program:
$ 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` due to previous error
Ekspresi di blok if
dievaluasi ke integer, dan ekspresi di blok else
dievaluasi ke string. Ini tidak akan berhasil karena variabel harus memiliki satu tipe, dan Rust perlu mengetahui pada waktu kompilasi tipe variabel number
tersebut, secara definitif. Mengetahui tipe number
memungkinkan kompiler memverifikasi tipe valid di mana pun kami menggunakan number
. Rust tidak akan dapat melakukan itu jika tipe dari number
hanya ditentukan pada saat runtime; kompiler akan lebih kompleks dan akan membuat lebih sedikit jaminan tentang kode jika harus melacak beberapa tipe hipotetis untuk variabel apa pun.
Pengulangan dengan Loop
Seringkali berguna untuk mengeksekusi blok kode lebih dari sekali. Untuk tugas ini, Rust menyediakan beberapa loop, yang akan dijalankan melalui kode di dalam loop body sampai akhir dan kemudian segera mulai kembali dari awal. Untuk bereksperimen dengan perulangan, mari buat proyek baru bernama loops.
Rust memiliki tiga jenis loop: loop
, while
, dan for
. Mari kita coba masing-masing.
Mengulang Kode dengan loop
Kata kunci loop
memberi tahu Rust untuk mengeksekusi blok kode berulang kali selamanya atau sampai Anda secara eksplisit menyuruhnya berhenti.
Sebagai contoh, ubah file src/main.rs di direktori loops Anda menjadi seperti ini:
Nama file: src/main.rs
fn main() {
loop {
println!("again!");
}
}
Saat kita menjalankan program ini, kita akan melihat again!
dicetak terus menerus sampai kita menghentikan program secara manual. Sebagian besar terminal mendukung pintasan keyboard ctrl-c untuk menginterupsi program yang macet terus-menerus. Cobalah:
$ cargo run
Compiling loops v0.1.0 (file:///projects/loops)
Finished dev [unoptimized + debuginfo] target(s) in 0.29s
Running `target/debug/loops`
again!
again!
again!
again!
^Cagain!
Simbol ^C
mewakili tempat Anda menekan ctrl-c. Anda mungkin atau mungkin tidak melihat kata again!
yang dicetak setelah ^C
, tergantung di mana kode berada di loop saat menerima sinyal interupsi.
Untungnya, Rust juga menyediakan cara untuk keluar dari loop menggunakan kode. Anda dapat menempatkan kata kunci break
di dalam loop untuk memberi tahu program kapan harus berhenti menjalankan loop. Ingatlah bahwa kita melakukan ini dalam permainan tebak-tebakan di bagian “Berhenti Setelah Menebak dengan Benar” “Quitting After a Correct Guess” pada Bab 2 untuk keluar dari program saat pengguna memenangkan permainan dengan menebak angka yang benar.
Kami juga menggunakan continue
dalam permainan tebak-tebakan, yang dalam satu loop memberi tahu program untuk melewati kode yang tersisa dalam iterasi loop ini dan melanjutkan ke iterasi berikutnya.
Mengembalikan Nilai dari Loop
Salah satu kegunaan loop
adalah mencoba kembali operasi yang Anda tahu mungkin gagal, seperti memeriksa apakah thread telah menyelesaikan tugasnya. Anda mungkin juga perlu meneruskan hasil operasi itu keluar dari loop ke seluruh kode Anda. Untuk melakukan ini, Anda bisa menambahkan nilai yang ingin Anda kembalikan setelah ekspresi break
yang Anda gunakan untuk menghentikan perulangan; nilai itu akan dikembalikan dari loop sehingga Anda dapat menggunakannya, seperti yang ditunjukkan 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, kami mendeklarasikan variabel bernama counter
dan menginisialisasi ke 0
. Kemudian kami mendeklarasikan variabel bernama result
untuk menampung nilai yang dikembalikan dari loop. Pada setiap iterasi loop, kita menambahkan 1
variabel counter, lalu memeriksa apakah counter
sama dengan 10
. Saat itu, kami menggunakan kata kunci break
dengan nilai counter * 2
. Setelah perulangan, kita menggunakan titik koma untuk mengakhiri pernyataan yang menetapkan nilai ke result
. Terakhir, kami mencetak nilai dalam result
, yang dalam hal ini adalah 20
.
Label Loop untuk Disambiguasi Antara Beberapa Loop
Jika Anda memiliki loop di dalam loop, break
dan continue
diterapkan ke loop terdalam pada saat itu. Secara opsional, Anda dapat menentukan label loop pada loop yang kemudian dapat Anda gunakan dengan break
atau continue
untuk menentukan bahwa kata kunci tersebut berlaku untuk loop berlabel, bukan loop terdalam. Label loop harus dimulai dengan kutip tunggal. Berikut adalah 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 luar memiliki label 'counting_up
, dan akan menghitung dari 0 hingga 2. Loop dalam tanpa label menghitung mundur dari 10 hingga 9. break
pertama yang tidak menentukan label hanya akan keluar dari loop di dalam. Pernyataan break 'counting_up;
akan keluar dari loop luar. Kode ini mencetak:
$ cargo run
Compiling loops v0.1.0 (file:///projects/loops)
Finished dev [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
Loop Bersyarat dengan while
Suatu program seringkali perlu mengevaluasi suatu kondisi dalam satu loop. Saat kondisinya true
, loop berjalan. Ketika kondisi berhenti menjadi true
, program memanggil break
, menghentikan perulangan. Dimungkinkan untuk menerapkan perilaku seperti ini menggunakan kombinasi loop
, if
, else
, dan break
; Anda dapat mencobanya sekarang dalam sebuah program, jika Anda mau. Namun, pola ini sangat umum sehingga Rust memiliki konstruksi bahasa bawaan untuknya, yang disebut loop while
. Dalam Daftar 3-3, kami menggunakan while
untuk mengulang program tiga kali, menghitung mundur setiap iterasi, dan kemudian, setelah pengulangan, cetak pesan dan keluar.
Nama file: src/main.rs
fn main() { let mut number = 3; while number != 0 { println!("{number}!"); number -= 1; } println!("LIFTOFF!!!"); }
Konstruk ini menghilangkan banyak sarang yang diperlukan jika Anda menggunakan loop
, if
, else
, dan break
, dan lebih jelas. Saat kondisi bernilai true
, kode berjalan; jika tidak, itu keluar dari loop.
Pengulangan melalui Koleksi dengan for
Anda dapat memilih untuk menggunakan konstruk while
untuk mengulang elemen koleksi, seperti array. Sebagai contoh, loop pada Daftar 3-4 mencetak setiap elemen dalam array a
.
Nama file: src/main.rs
fn main() { let a = [10, 20, 30, 40, 50]; let mut index = 0; while index < 5 { println!("the value is: {}", a[index]); index += 1; } }
Di sini, kode dihitung melalui elemen-elemen dalam array. Ini dimulai pada index 0
, dan kemudian berulang hingga mencapai indeks terakhir dalam array (yaitu, ketika index < 5
tidak lagi true
). Menjalankan kode ini akan mencetak setiap elemen dalam array:
$ cargo run
Compiling loops v0.1.0 (file:///projects/loops)
Finished dev [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
Kelima nilai array muncul di terminal, seperti yang diharapkan. Meskipun index
akan mencapai nilai 5
di beberapa titik, loop berhenti mengeksekusi sebelum mencoba mengambil nilai keenam dari array.
Namun, pendekatan ini rawan kesalahan; dapat menyebabkan program kita menjadi panik jika nilai indeks atau kondisi pengujian salah. Misalnya, jika Anda mengubah definisi array a
menjadi empat elemen tetapi lupa memperbarui kondisinya menjadi while index < 4
, kode akan panik. Ini juga lambat, karena kompiler menambahkan kode runtime untuk melakukan pemeriksaan kondisional apakah indeks berada dalam batas array pada setiap iterasi melalui loop.
Sebagai alternatif yang lebih ringkas, Anda bisa menggunakan perulangan for
dan mengeksekusi beberapa kode untuk setiap item dalam koleksi. Loop for
terlihat seperti kode pada Daftar 3-5.
Nama file: src/main.rs
fn main() { let a = [10, 20, 30, 40, 50]; for element in a { println!("the value is: {element}"); } }
Saat kita menjalankan kode ini, kita akan melihat output yang sama seperti pada Daftar 3-4. Lebih penting lagi, kami sekarang telah meningkatkan keamanan kode dan menghilangkan kemungkinan bug yang mungkin dihasilkan dari melampaui akhir array atau tidak cukup jauh dan kehilangan beberapa item.
Dengan menggunakan for
, Anda tidak perlu mengingat untuk mengubah kode lain jika Anda mengubah jumlah nilai dalam array, seperti yang Anda lakukan dengan metode yang digunakan pada Daftar 3-4.
Keamanan dan keringkasan for
menjadikannya konstruksi loop yang paling umum digunakan di Rust. Bahkan dalam situasi di mana Anda ingin menjalankan beberapa kode beberapa kali, seperti dalam contoh hitung mundur yang menggunakan perulangan while
di Daftar 3-3, kebanyakan Rustacean akan menggunakan perulangan for
. Cara melakukannya adalah dengan menggunakan Range
, yang disediakan oleh pustaka standar, yang menghasilkan semua angka secara berurutan mulai dari satu angka dan diakhiri sebelum angka lainnya.
Berikut tampilan hitungan mundur menggunakan perulangan for
dan metode lain yang belum kita bicarakan, rev
, untuk membalikkan rentang:
Nama file: src/main.rs
fn main() { for number in (1..4).rev() { println!("{number}!"); } println!("LIFTOFF!!!"); }
Kode ini sedikit lebih bagus, bukan?
Ringkasan
Anda berhasil! Ini adalah bab yang cukup besar: Anda belajar tentang variabel, tipe data skalar dan compound, fungsi, komentar, ekspresi if
, dan loop! Untuk berlatih dengan konsep yang dibahas dalam bab ini, cobalah membuat program untuk melakukan hal berikut:
- Mengkonversi suhu antara Fahrenheit dan Celsius.
- Hasilkan angka Fibonacci ke-n .
- Cetak lirik lagu Natal “The Twelve Days of Christmas”, memanfaatkan pengulangan dalam lagu tersebut.
Saat Anda siap untuk melanjutkan, kita akan berbicara tentang konsep di Rust yang tidak umum ada dalam bahasa pemrograman lain: kepemilikan (ownership).