Sejauh ini seluruh kode yang kita tulis hidup di dalam satu fungsi bernama main. Aplikasi nyata tidak bisa berkembang dengan cara itu: teks sambutan, menu pilihan, dan laporan penjualan akan menumpuk menjadi satu blok raksasa yang sulit dibaca dan diubah. Fungsi memecah program menjadi bagian-bagian kecil yang punya nama, sehingga kita cukup memanggil nama tersebut setiap kali butuh pekerjaan yang sama diulang. Pada Chapter ini kita akan belajar mendefinisikan fungsi pertama dengan kata kunci fn, memberi nama sesuai konvensi Rust, dan memanggil fungsi tersebut dari fungsi lain.
Masalah Kode yang Menumpuk di main #
Bayangkan program kasir yang harus mencetak sambutan, menu, dan struk pembelian. Kalau seluruh logika tersebut ditulis berurutan di dalam main, tiga masalah muncul dengan cepat. Pertama, teks sambutan yang dipakai di dua tempat harus disalin ulang, sehingga setiap perubahan teks harus dilakukan dua kali. Kedua, nama variabel sementara seperti teks dan nomor saling menimpa karena berbagi satu ruang yang sama. Ketiga, pembaca kode harus memahami seluruh isi main dari awal hingga akhir sebelum tahu bagian mana yang mencetak struk.
Fungsi menyelesaikan ketiga masalah tersebut sekaligus. Sebuah fungsi adalah blok kode bernama yang diberi tugas spesifik, misalnya mencetak menu atau menghitung total. Blok main tinggal memanggil nama-nama fungsi itu secara berurutan, dan setiap fungsi dapat dipanggil berkali-kali tanpa menyalin kode.
Anatomi Fungsi Rust #
Fungsi di Rust didefinisikan dengan kata kunci fn, diikuti nama fungsi, tanda kurung, lalu sepasang kurung kurawal yang membungkus tubuh fungsi (function body). Berikut bentuk paling sederhana:
fn sapa() {
println!("Selamat datang di aplikasi kasir!");
}Fungsi di atas bernama sapa, tidak menerima masukan apa pun (ditandai tanda kurung kosong), dan tubuhnya berisi satu pemanggilan println!. Bagian di dalam kurung kurawal hanya dijalankan ketika fungsi dipanggil, bukan saat fungsi didefinisikan. Rust membaca definisi fungsi untuk mencatat nama dan bentuknya saja; eksekusi baru terjadi saat ada pemanggilan.
Untuk memanggil fungsi, kita cukup menuliskan nama fungsi tersebut diikuti tanda kurung dan titik koma, misalnya sapa();. Pemanggilan boleh dilakukan dari fungsi mana pun, termasuk dari dalam main maupun dari dalam fungsi lain.
Konvensi Penamaan snake_case #
Rust menganut konvensi penamaan snake_case untuk fungsi: seluruh huruf ditulis kecil, dan setiap kata dipisahkan garis bawah. Konvensi ini bukan sekadar gaya, karena kompiler Rust benar-benar memeriksa penamaan tersebut.
| Bentuk nama | Penilaian Rust |
|---|---|
tampilkan_menu | Sesuai konvensi, tanpa peringatan |
hitung_total_belanja | Sesuai konvensi, tanpa peringatan |
TampilkanMenu | Memicu peringatan non_snake_case |
HitungTotal | Memicu peringatan non_snake_case |
Kalau kita mendefinisikan fungsi bernama TampilkanMenu, kompiler Rust tetap menghasilkan program yang berjalan, tetapi kompiler menampilkan peringatan seperti ini:
warning: function `TampilkanMenu` should have a snake case name
|
1 | fn TampilkanMenu() {
| ^^^^^^^^^^^^^ help: convert the identifier to snake case: `tampilkan_menu`Peringatan tersebut bukan galat; program tetap dikompilasi. Namun kode produksi yang sehat selalu bebas peringatan, jadi kita sebaiknya mengikuti konvensi snake_case sejak awal.
Memanggil Fungsi dari main #
Sekarang kita rakit dua fungsi sederhana dan panggil keduanya dari main. Simpan kode berikut sebagai berkas kasir.rs, lalu kompilasi dan jalankan:
fn sapa() {
println!("Selamat datang di aplikasi kasir!");
}
fn tampilkan_menu() {
println!("1. Tambah barang");
println!("2. Hapus barang");
}
fn main() {
sapa();
tampilkan_menu();
}Output yang Dihasilkan:
Selamat datang di aplikasi kasir!
1. Tambah barang
2. Hapus barangMari kita telaah apa yang terjadi pada baris-baris kode di atas:
- Baris 1 (
fn sapa() {): Kompiler mencatat bahwa ada fungsi bernamasapayang tidak menerima masukan dan tubuhnya diapit kurung kurawal. - Baris 5 (
fn tampilkan_menu() {): Kompiler mencatat fungsi kedua bernamatampilkan_menudengan bentuk yang sama. - Baris 10 (
fn main() {): Kompiler menandaimainsebagai titik masuk program, sehingga eksekusi selalu dimulai dari fungsi ini. - Baris 11 (
sapa();): Runtime menjalankan tubuh fungsisapa, mencetak satu baris sambutan, lalu kembali kemain. - Baris 12 (
tampilkan_menu();): Runtime menjalankan tubuh fungsitampilkan_menu, mencetak dua baris menu, lalu kembali kemain. Setelah baris ini,mainselesai dan program berakhir.
Perhatikan urutan baris pada output mengikuti urutan pemanggilan di main, bukan urutan definisi fungsi di berkas. Definisi hanyalah katalog; pemanggilan adalah perintah eksekusi.
Memanggil Fungsi dari Fungsi Lain #
Fungsi juga boleh memanggil fungsi lain, tidak hanya dipanggil oleh main. Pola ini memungkinkan kita menyusun laporan yang terdiri dari bagian kepala, isi, dan kaki:
fn print_footer() {
println!("==========================");
}
fn print_report() {
print_header();
println!("Total: Rp150000");
print_footer();
}
fn print_header() {
println!("=== Laporan Penjualan ===");
}
fn main() {
print_report();
}Output yang Dihasilkan:
=== Laporan Penjualan ===
Total: Rp150000
==========================Mari kita telaah alur pemanggilan pada kode di atas:
- Baris 15 (
fn main() {): Eksekusi program dimulai darimain. - Baris 16 (
print_report();): Runtime berpindah ke fungsiprint_reportdan mulai menjalankan tubuhnya baris demi baris. - Baris 6 (
print_header();): Di dalamprint_report, pemanggilan ini menjalankan fungsiprint_headeryang terletak setelahprint_reportdi berkas. Posisi definisi yang belakangan tidak menjadi masalah. - Baris 7–8: Baris teks total dan pemanggilan
print_footer()mencetak isi serta kaki laporan secara berurutan, laluprint_reportselesai dan kontrol kembali kemain.
Contoh di atas memperlihatkan sifat penting Rust: urutan penulisan definisi fungsi di berkas bebas. print_report dapat memanggil print_header walau print_header baru dideklarasikan di baris ke-11, karena kompiler membaca seluruh berkas sebelum menghasilkan program.
Ringkasan #
Fungsi didefinisikan dengan kata kunci fn, diberi nama snake_case, dan tubuhnya dijalankan hanya saat dipanggil dengan nama_fungsi();. Eksekusi program selalu dimulai dari main, dan setiap fungsi boleh memanggil fungsi lain tanpa memedulikan urutan definisi. Dengan bekal ini, kita siap mengirim data ke dalam fungsi melalui parameter dan argument pada Chapter berikutnya.