Setiap aplikasi memegang sejumlah nilai yang tidak boleh berubah sampai kapan pun: nama aplikasi, tarif pajak, batas maksimal pesanan, atau jumlah detik dalam sehari. Menuliskan angka-angka itu berserakan di kode membuat perubahan satu nilai menjadi perburuan rawan salah. Variable immutable yang kita pelajari sebelumnya menutup kemungkinan perubahan secara tidak sengaja, tetapi variable tetap dihitung saat program berjalan dan hanya hidup di dalam scope-nya. Rust menyediakan konstanta (const) untuk nilai yang sudah diketahui dan dipatok sejak program dikompilasi, bahkan sebelum program mulai berjalan. Pada Chapter ini kita akan mendeklarasikan konstanta, memahami evaluasinya saat kompilasi, dan mengenali batas-batasnya.

Mendeklarasikan Konstanta #

Konstanta dideklarasikan dengan kata kunci const, nama yang ditulis huruf besar semua dengan pemisah garis bawah (konvensi UPPER_SNAKE_CASE), serta tipe yang wajib dicantumkan secara eksplisit. Berbeda dari let, const lazim ditempatkan di bagian atas berkas sehingga seluruh fungsi di berkas itu bisa memakainya:

rust
const NAMA_TOKO: &str = "TokoKita";
const HARGA_SATUAN: u64 = 85_000;
const PAJAK_PERSEN: u64 = 11;

fn main() {
    let jumlah_pesanan: u64 = 5;

    let subtotal = HARGA_SATUAN * jumlah_pesanan;
    let pajak = subtotal * PAJAK_PERSEN / 100;
    let total_bayar = subtotal + pajak;

    println!("Toko: {}", NAMA_TOKO);
    println!("Subtotal: Rp{}", subtotal);
    println!("Pajak: Rp{}", pajak);
    println!("Total bayar: Rp{}", total_bayar);
}

Output yang Dihasilkan:

text
Toko: TokoKita
Subtotal: Rp425000
Pajak: Rp46750
Total bayar: Rp471750

Mari kita bedah kode di atas baris per baris:

  1. Baris 1 (const NAMA_TOKO: &str = "TokoKita";): konstanta string dideklarasikan di luar fungsi; tanda &str menyatakan tipe teks, dan nama memakai huruf besar semua.
  2. Baris 2-3 (const HARGA_SATUAN ... const PAJAK_PERSEN ...): dua konstanta bilangan bertipe u64 siap dipakai di seluruh berkas; tipe eksplisit membuat pembaca tahu persis rentang nilainya.
  3. Baris 6 (let jumlah_pesanan: u64 = 5;): jumlah pesanan bersifat data masukan yang berubah tiap transaksi, sehingga tepat menjadi variable, bukan konstanta.
  4. Baris 8-10 (let subtotal ... let pajak ... let total_bayar ...): ketiga baris menghitung hasil dengan memakai konstanta; 425000 dikalikan 11 lalu dibagi 100 menghasilkan pajak 46750.

Penamaan huruf besar bukan sekadar gaya. Saat kita melihat PAJAK_PERSEN di tengah rumus, bentuk penulisannya langsung memberi sinyal bahwa nilai tersebut bersumber dari satu tempat deklarasi dan tidak akan berubah.

Nilai Dievaluasi saat Kompilasi #

Nilai konstanta dihitung oleh kompilator, bukan oleh program yang sedang berjalan. Artinya, ekspresi di sisi kanan const boleh memuat operasi terhadap konstanta lain, selama seluruh bahannya sudah pasti saat kompilasi. Pola ini pas untuk menurunkan satuan yang saling berkaitan:

rust
const DETIK_PER_MENIT: u32 = 60;
const DETIK_PER_JAM: u32 = DETIK_PER_MENIT * 60;
const DETIK_PER_HARI: u32 = DETIK_PER_JAM * 24;

fn main() {
    println!("Detik per jam : {}", DETIK_PER_JAM);
    println!("Detik per hari: {}", DETIK_PER_HARI);
}

Output yang Dihasilkan:

text
Detik per jam : 3600
Detik per hari: 86400

Mari kita telaah mekanisme di baliknya:

  1. Baris 1 (const DETIK_PER_MENIT: u32 = 60;): kompilator mencatat konstanta dasar bernilai 60.
  2. Baris 2 (const DETIK_PER_JAM: u32 = DETIK_PER_MENIT * 60;): kompilator menghitung 60 * 60 saat kompilasi dan menyimpan hasil 3600; program hasil kompilasi tidak lagi melakukan perkalian apa pun.
  3. Baris 3 (const DETIK_PER_HARI: u32 = DETIK_PER_JAM * 24;): hasil penurunan berantai melahirkan 86400 yang juga sudah jadi angka final di dalam program biner.

Jika suatu saat definisi menit berubah, seluruh konstanta turunannya ikut terhitung ulang secara otomatis pada kompilasi berikutnya. Perubahan cukup dilakukan di satu titik dan konsistensinya dijamin kompilator.

Batasan const #

Karena nilainya harus pasti saat kompilasi, const tidak menerima nilai hasil input pengguna, hasil pembacaan berkas, atau isi variable runtime. Kode berikut mencoba memasukkan isi variable ke dalam konstanta dan ditolak:

rust
fn main() {
    let stok_gudang = 150;
    const STOK_AWAL: u32 = stok_gudang;

    println!("Stok awal: {}", STOK_AWAL);
}

Pesan galatnya berbunyi:

text
error[E0435]: attempt to use a non-constant value in a constant
 --> main.rs:3:28
  |
3 |     const STOK_AWAL: u32 = stok_gudang;
  |                            ^^^^^^^^^^^ non-constant value
  |
help: consider using `let` instead of `const`
  |
3 -     const STOK_AWAL: u32 = stok_gudang;
3 +     let STOK_AWAL: u32 = stok_gudang;
  |

Kompilator menegaskan bahwa stok_gudang bukan nilai konstan, lalu menyarankan let sebagai gantinya. Batasan kedua menyangkut penempatan: let hanya sah di dalam fungsi atau blok, sehingga variable global ala PHP tidak bisa dibuat dengan let di luar fungsi:

rust
let nama_toko = "TokoKita";

fn main() {
    println!("Toko: {}", nama_toko);
}

Pesan galatnya jelas menolak let di posisi tersebut:

text
error: expected item, found keyword `let`
 --> main.rs:1:1
  |
1 | let nama_toko = "TokoKita";
  | ^^^
  | |
  | `let` cannot be used for global variables
  | help: consider using `static` or `const` instead of `let`
  |
  = note: for a full list of items that can appear in modules, see <https://doc.rust-lang.org/reference/items.html>

error: aborting due to 1 previous error

Batasan ketiga adalah kewajiban tipe eksplisit. Kode berikut menghilangkan tipe dan ditolak karena const tidak memakai type inference:

rust
const MAKS_STOK = 100_000;

fn main() {
    println!("Maks stok: {}", MAKS_STOK);
}

Pesan galatnya berbunyi:

text
error: missing type for `const` item
 --> main.rs:1:16
  |
1 | const MAKS_STOK = 100_000;
  |                ^ help: provide a type for the constant: `: i32`

error: aborting due to 1 previous error

Kompilator bahkan menyarankan : i32, tetapi keputusan tipe tetap milik kita agar rentang nilai konstanta terdokumentasi secara eksplisit.

Perbedaan const dan let #

Aspekconstlet
Waktu evaluasisaat kompilasisaat program berjalan
Tipe eksplisitwajibopsional (bisa diinferensikan)
Penempatanluar atau dalam fungsihanya dalam fungsi atau blok
Mutabilitasselalu immutableimmutable bawaan, bisa mut
Sumber nilaihanya ekspresi konstantanilai apa pun, termasuk input runtime

Ringkasan #

Konstanta dideklarasikan dengan const, ditulis UPPER_SNAKE_CASE, wajib bertipe eksplisit, dan boleh ditempatkan di luar fungsi agar dipakai lintas scope. Nilainya dievaluasi kompilator sehingga penurunan konstanta berantai otomatis terhitung ulang saat kompilasi. Sebaliknya, const menolak nilai runtime, let tidak sah di level modul, dan tipe tidak boleh dihilangkan. Dengan aturan nilai yang kini lengkap, kita lanjut ke operator aritmetika dan perbandingan untuk mulai menghitung.