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:
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:
Toko: TokoKita
Subtotal: Rp425000
Pajak: Rp46750
Total bayar: Rp471750Mari kita bedah kode di atas baris per baris:
- Baris 1 (
const NAMA_TOKO: &str = "TokoKita";): konstanta string dideklarasikan di luar fungsi; tanda&strmenyatakan tipe teks, dan nama memakai huruf besar semua. - Baris 2-3 (
const HARGA_SATUAN ... const PAJAK_PERSEN ...): dua konstanta bilangan bertipeu64siap dipakai di seluruh berkas; tipe eksplisit membuat pembaca tahu persis rentang nilainya. - Baris 6 (
let jumlah_pesanan: u64 = 5;): jumlah pesanan bersifat data masukan yang berubah tiap transaksi, sehingga tepat menjadi variable, bukan konstanta. - Baris 8-10 (
let subtotal ... let pajak ... let total_bayar ...): ketiga baris menghitung hasil dengan memakai konstanta;425000dikalikan11lalu dibagi100menghasilkan pajak46750.
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:
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:
Detik per jam : 3600
Detik per hari: 86400Mari kita telaah mekanisme di baliknya:
- Baris 1 (
const DETIK_PER_MENIT: u32 = 60;): kompilator mencatat konstanta dasar bernilai60. - Baris 2 (
const DETIK_PER_JAM: u32 = DETIK_PER_MENIT * 60;): kompilator menghitung60 * 60saat kompilasi dan menyimpan hasil3600; program hasil kompilasi tidak lagi melakukan perkalian apa pun. - Baris 3 (
const DETIK_PER_HARI: u32 = DETIK_PER_JAM * 24;): hasil penurunan berantai melahirkan86400yang 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:
fn main() {
let stok_gudang = 150;
const STOK_AWAL: u32 = stok_gudang;
println!("Stok awal: {}", STOK_AWAL);
}Pesan galatnya berbunyi:
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:
let nama_toko = "TokoKita";
fn main() {
println!("Toko: {}", nama_toko);
}Pesan galatnya jelas menolak let di posisi tersebut:
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 errorBatasan ketiga adalah kewajiban tipe eksplisit. Kode berikut menghilangkan tipe dan ditolak karena const tidak memakai type inference:
const MAKS_STOK = 100_000;
fn main() {
println!("Maks stok: {}", MAKS_STOK);
}Pesan galatnya berbunyi:
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 errorKompilator bahkan menyarankan : i32, tetapi keputusan tipe tetap milik kita agar rentang nilai konstanta terdokumentasi secara eksplisit.
Perbedaan const dan let #
| Aspek | const | let |
|---|---|---|
| Waktu evaluasi | saat kompilasi | saat program berjalan |
| Tipe eksplisit | wajib | opsional (bisa diinferensikan) |
| Penempatan | luar atau dalam fungsi | hanya dalam fungsi atau blok |
| Mutabilitas | selalu immutable | immutable bawaan, bisa mut |
| Sumber nilai | hanya ekspresi konstanta | nilai 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.