Pada Chapter sebelumnya kita mengalami kejadian aneh: satu titik koma membuat fungsi gagal dikompilasi. Kompiler menyebut tubuh fungsi “mengembalikan ()” padahal baris terakhirnya berisi total / 10 yang jelas-jelas angka. Kejadian itu bukan keanehan, melainkan pintu masuk ke salah satu konsep paling mendasar di Rust: pembedaan antara expression dan statement. Bab-bab sebelumnya kita memakai istilah ini asal tanpa definisi. Pada Chapter ini kita akan mendefinisikan keduanya, melihat bagaimana kompiler memperlakukan keduanya secara berbeda, dan memanfaatkan pemahaman tersebut untuk menulis kode yang lebih rapat.

Statement Adalah Instruksi, Expression Adalah Nilai #

Definisi keduanya singkat tetapi berdampak besar:

  • Statement adalah instruksi yang melakukan sesuatu dan tidak menghasilkan nilai yang bisa disimpan. Statement diakhiri titik koma. Contohnya: let untuk membuat variabel, dan println! untuk mencetak.
  • Expression adalah potongan kode yang dievaluasi menjadi sebuah nilai. Expression tidak butuh titik koma; kalau ditambahkan titik koma, expression berubah menjadi statement yang membuang nilainya.

Contoh expression mencakup literal seperti 25000 dan "Kopi Susu", operasi aritmetika seperti harga * jumlah, pemanggilan fungsi seperti hitung_diskon(total), dan — nanti kita bahas tuntas di Chapter berikutnya — blok { } itu sendiri.

Mari kita bedah program sederhana dengan kacamata baru ini:

rust
fn main() {
    let harga = 25000;
    let jumlah = 4;
    let total = harga * jumlah;
    let rincian = "Kopi Susu";

    println!("{} x {} = Rp{}", rincian, jumlah, total);
}

Output yang Dihasilkan:

text
Kopi Susu x 4 = Rp100000

Mari kita klasifikasikan baris-baris kode di atas:

  1. Baris 1–2 (let harga = 25000; dan let jumlah = 4;): Keduanya adalah statement — instruksi membuat variabel. Meski begitu, sisi kanannya (25000 dan 4) adalah expression bernilai yang dievaluasi lebih dulu sebelum diikat ke variabel.
  2. Baris 4 (let total = harga * jumlah;): Baris ini statement let yang sisi kanannya adalah expression kompleks harga * jumlah. Kompiler membaca nilai harga (25000) dan jumlah (4), mengalikan, lalu let mengikat hasil 100000 ke total.
  3. Baris 5 (let rincian = "Kopi Susu";): String literal adalah expression. Statement let mengikat nilai teks tersebut ke rincian.
  4. Baris 7 (println!(...);): Macro println! mencetak ke layar dan menghasilkan nilai kosong (). Karena hasilnya tidak berguna, pemanggilan ini diperlakukan sebagai statement dengan titik koma.

Pola yang muncul: hampir semua baris di Rust adalah “statement di luar, expression di dalam”. Bagian kanan let adalah dunia expression yang menghasilkan nilai; bagian let-nya sendiri adalah statement yang menangkap nilai tersebut.

Expression Bisa Ditempatkan di Mana Nilai Dibutuhkan #

Aturan praktisnya satu kalimat: di mana pun sebuah nilai bisa berada, di situ pula boleh ada expression yang mengevaluasi menjadi nilai tersebut. Karena sisi kanan let menerima nilai, sisi kanan let juga menerima expression bentuk apa pun. Termasuk expression if:

rust
fn main() {
    let stok = 3;
    let status = if stok > 0 { "tersedia" } else { "habis" };

    println!("Status barang: {}", status);
}

Output yang Dihasilkan:

text
Status barang: tersedia

Mari kita telaah cara kerja kode di atas:

  1. Baris 2 (let stok = 3;): Statement let standar mengikat nilai 3 ke stok.
  2. Baris 3 (let status = if stok > 0 { ... } else { ... };): Di banyak bahasa, if hanya mengatur alur. Di Rust, if adalah expression yang menghasilkan nilai: cabang { "tersedia" } dan { "habis" } masing-masing adalah blok yang mengembalikan string. Karena stok bernilai 3 (lebih besar dari 0), expression if mengevaluasi menjadi "tersedia", lalu let mengikatnya ke status.
  3. Baris 5 (println!("Status barang: {}", status);): Nilai hasil expression if dicetak seperti nilai variabel biasa.

Idiom let status = if ... { ... } else { ... }; menggantikan pola lama “set variabel default, lalu timpa di dalam if” yang empat-baris. Hasilnya lebih pendek dan variabel status dijamin terisi dalam satu tempat. Syaratnya satu: kedua cabang harus menghasilkan tipe yang sama, karena status hanya boleh memegang satu tipe.

Statement Tidak Bisa Ditempatkan di Mana Nilai Dibutuhkan #

Sisi kebalikannya juga berlaku: statement tidak menghasilkan nilai, jadi statement tidak boleh menempati posisi nilai. Pemula sering mencoba hal berikut:

rust
fn main() {
    let y = (let x = 5);
}

Kompilasi gagal dengan galat sintaks:

text
error: expected expression, found `let` statement
 --> src/main.rs:2:14
  |
2 |     let y = (let x = 5);
  |              ^^^

Pesan tersebut menegaskan aturannya: posisi setelah tanda sama dengan adalah dunia expression, dan let x = 5 adalah statement yang tidak punya nilai untuk ditawarkan. Statement let tidak bisa ditumpuk ke dalam statement let lain, dan tidak bisa dijadikan argument pemanggilan fungsi. Kompiler menjaga perbatasan ini secara ketat.

Titik Koma Sebagai Tombol Transformasi #

Kita kini punya kosakata untuk menjelaskan jebakan titik koma dari Chapter sebelumnya. Titik koma bukan sekadar hiasan akhir baris; titik koma adalah tombol yang mengubah expression menjadi statement dan membuang nilainya:

KodeKategoriHasil
total / 10ExpressionNilai hasil pembagian
total / 10;StatementNilai dibuang, menghasilkan ()
harga * jumlahExpressionNilai hasil perkalian
{ total - diskon }Expression blokNilai baris terakhir blok
let total = 95000;StatementTidak bernilai

Baris terakhir tubuh fungsi adalah titik rawan. Fungsi dengan tipe kembali -> i64 menuntut baris terakhirnya berupa expression i64; menambahkan titik koma mengubahnya menjadi statement bernilai (), sehingga muncul galat mismatched types: expected i64, found (). Sekarang kita tahu persis mengapa satu karakter itu berkuasa.

Ringkasan #

Statement adalah instruksi tanpa nilai yang diakhiri titik koma, sedangkan expression adalah potongan kode yang mengevaluasi menjadi nilai dan bisa ditempatkan di posisi mana pun nilai diperlukan. Titik koma mengubah expression menjadi statement dan membuang nilainya; let adalah statement meski sisi kanannya expression; dan if di Rust adalah expression yang nilainya bisa diikat ke variabel. Satu entitas terakhir belum kita sentuh: blok { } ternyata juga expression penuh — dan itulah bintang utama Chapter berikutnya.