Dua kebutuhan program yang tampak sepele justru sengaja tidak diurus standard library: pengacakan angka dan penghitungan tanggal. Kedua topik tersebut penuh jebakan yang tersembunyi — pengacakan naif dengan sisa bagi menghasilkan distribusi miring, dan aritmetika tanggal harus mengakomodasi tahun kabisat, jumlah hari per bulan, hingga zona waktu — sehingga tim Rust memilih tidak membekulkannya di std dan membiarkan komunitas mengembangkan solusi yang matang. Hasilnya dua crate yang nyaris menjadi bagian tidak resmi dari standard library: rand untuk angka acak dan chrono untuk tanggal dan waktu. Pada Chapter ini kita memakai keduanya dalam satu proyek masing-masing, mulai dari lempar dadu dan pemilihan menu acak sampai membaca tanggal rilis dari teks dan mencetaknya dalam format Indonesia.
rand: Angka Acak Terdistribusi Seragam #
Mari kita mulai dari masalah konkretnya. Menghasilkan angka dadu dengan pola % 6 + 1 dari sembarang sumber angka terlihat benar, tetapi pola tersebut menghasilkan distribusi miring: jika rentang sumber tidak habis dibagi enam, satu sisi mata dadi muncul lebih sering. Generator yang baik juga butuh seed yang tidak bisa ditebak — jam sistem dan alamat memori adalah sumber lemah yang bisa dikira lawan. Algoritma yang lolos uji statistik dan kriptografi memang ada, tetapi mengimplementasikannya sendiri justru contoh klasik keputusan yang seharusnya diserahkan ke library teruji.
Crate rand mengurus rantai penuh kebutuhan tersebut. Fungsi rng() memberi kita generator siap pakai yang diunggulkan dari entropi sistem operasi dan memakai inti keluarga algoritma ChaCha — penghasil angka semu yang tergolong aman secara kriptografis — sehingga hasilnya layak dipakai untuk undian, pengacakan data uji, dan penggunaan umum lainnya. Di atas generator itu, metode random_range() mengambil sampel dari rentang apa pun dengan distribusi seragam yang benar, dan trait IndexedRandom menyediakan choose() untuk memilih satu elemen dari koleksi secara adil.
Buat proyek permainan dadu dan tambahkan dependency:
cargo new dadu_acak
cd dadu_acak
cargo add rand[package]
name = "dadu_acak"
version = "0.1.0"
edition = "2024"
[dependencies]
rand = "0.10.3"Kode src/main.rs melempar dua dadu lalu memilih menu makan siang secara acak:
use rand::seq::IndexedRandom;
use rand::{rng, RngExt};
fn main() {
let mut lempar = rng();
println!("Dadu pertama : {}", lempar.random_range(1..=6));
println!("Dadu kedua : {}", lempar.random_range(1..=6));
let menu = vec![
String::from("Nasi goreng"),
String::from("Mie ayam"),
String::from("Sate ayam"),
String::from("Bakso kuah"),
];
let pilihan = menu.choose(&mut lempar).expect("menu tidak kosong");
println!("Menu acak hari ini: {pilihan}");
}Jalankan dua kali berturut-turut:
cargo run
cargo runOutput yang Dihasilkan:
Dadu pertama : 1
Dadu kedua : 1
Menu acak hari ini: Bakso kuahDadu pertama : 5
Dadu kedua : 3
Menu acak hari ini: Nasi gorengMari kita bedah kode di atas:
- Baris 1 (
use rand::seq::IndexedRandom;): membawa trait pemilik metodechoose()— metode yang didefinisikan pada koleksi terindeks sepertiVecdan slice, bukan pada generatornya. - Baris 2 (
use rand::{rng, RngExt};): mengimpor fungsirng()pembuat generator dan traitRngExtpemilik metoderandom_range(). Seperti biasa di Rust, metode hanya bisa dipanggil bila trait-nya berada di scope. - Baris 5 (
let mut lempar = rng();): membuat satu generator yang dipakai ulang sepanjang program. Katamutwajib karena setiap pengambilan angka memajukan keadaan internal generator. - Baris 6-7 (
lempar.random_range(1..=6)): rentang inklusif1..=6menghasilkan angka 1 sampai 6 dengan peluang sama rata — distribusi seragamnya ditangani internal rand, bukan sisa bagi buatan kita. - Baris 9-14 (
let menu = vec![...]): koleksi pilihan menu sebagaiVec<String>—choose()bekerja pada koleksi apa pun yang bisa diindeks. - Baris 15 (
menu.choose(&mut lempar)): method dipanggil pada koleksi dengan generator sebagai argumen — arah yang sengaja dibalik rand pada versi baru. HasilnyaOption<&String>karena koleksi kosong tidak punya anggota untuk dipilih;expect()aman dipakai karena kita melihat menu punya empat anggota. - Baris 16 (
println!("Menu acak hari ini: {pilihan}");):pilihanbertipe&Stringdan tetap tercetak rapi karenaDisplayuntukStringberlaku juga pada referensinya.
chrono: Tanggal dengan Kesadaran Kalender #
Masalah kedua: std::time hanya mengenal instan — SystemTime dan Instant adalah titik waktu tanpa kalender. Menjawab “tanggal berapa tiga puluh hari dari sekarang?” dengan instan mentah memaksa kita mengubah durasi jadi milidetik lalu membagi-bagi sendiri sambil berdoa tidak melewati akhir bulan atau tahun kabisat. Aplikasi nyata butuh tipe yang memahami kalender: tahun, bulan, hari, zona waktu, dan pemformatan untuk dibaca manusia.
Crate chrono menyusun dunia waktu dari tipe-tipe yang jujur soal informasi yang dibawa. NaiveDate adalah tanggal tanpa jam dan tanpa zona waktu — pas untuk ulang tahun dan tanggal rilis. Local::now() menghasilkan DateTime<Local>: waktu lengkap yang sadar zona waktu sesuai pengaturan mesin. Metode parse_from_str() membaca teks menjadi tanggal dengan penanda format ala strftime, dan format() mencetak kembali dengan penanda yang sama.
Proyek kedua kita mengelola agenda rilis. Buat proyek dan pasang chrono:
cargo new agenda_harian
cd agenda_harian
cargo add chrono[package]
name = "agenda_harian"
version = "0.1.0"
edition = "2024"
[dependencies]
chrono = "0.4.45"Kode src/main.rs mencetak tanggal hari ini, lalu mem-parsing tanggal rilis tetap dan menyusunnya dalam format Indonesia:
use chrono::{Datelike, Local, NaiveDate};
fn main() {
let sekarang = Local::now();
println!("Hari ini : {}", sekarang.format("%Y-%m-%d"));
println!("Jam setempat : {}", sekarang.format("%H:%M"));
let tanggal = NaiveDate::parse_from_str("2026-10-13", "%Y-%m-%d").expect("tanggal valid");
println!("Tanggal parse : {}", tanggal.format("%d/%m/%Y"));
let nama_bulan = [
"Januari", "Februari", "Maret", "April", "Mei", "Juni",
"Juli", "Agustus", "September", "Oktober", "November", "Desember",
];
println!(
"Agenda rilis : {} {} {}",
tanggal.day(),
nama_bulan[(tanggal.month() - 1) as usize],
tanggal.year()
);
}Output yang Dihasilkan:
Hari ini : 2026-10-13
Jam setempat : 23:00
Tanggal parse : 13/10/2026
Agenda rilis : 13 Oktober 2026Dua baris pertama pada output tersebut akan berbeda di mesin kita masing-masing: Local::now() membaca jam dinding mesin tempat program berjalan, dan output di atas dicatat pada mesin pengujian bertanggal 13 Oktober 2026 dengan zona waktu UTC pukul 23:00. Jalankan di mesin berzona WIB dan hasilnya akan menampilkan jam tujuh jam lebih awal; jalankan bulan depan dan tanggalnya ikut maju. Dua baris terakhir bersifat tetap karena berasal dari parsing teks "2026-10-13" yang sama di mana pun program dijalankan.
Mari kita bedah kode di atas:
- Baris 1 (
use chrono::{Datelike, Local, NaiveDate};):Localadalah modul zona waktu mesin,NaiveDatetipe tanggal tanpa zona, dan traitDatelikepemilik metodeday(),month(), danyear(). - Baris 4 (
let sekarang = Local::now();): mengambil waktu sekarang dengan zona waktu mesin. Hasilnya bertipeDateTime<Local>— sadar zona, bukan waktu UTC mentah. - Baris 5-6 (
sekarang.format("%Y-%m-%d")): pemformatan memakai penandastrftime:%Ytahun empat digit,%mbulan dua digit,%dtanggal dua digit,%Hjam,%Mmenit. Methodformat()mengembalikan nilai yang bisa langsung dicetak. - Baris 8 (
NaiveDate::parse_from_str("2026-10-13", "%Y-%m-%d")): membaca teks menjadiNaiveDatedengan penanda format yang sama seperti pemformatan — simetri yang membuat kode bolak-balik parse-format mudah ditulis. Kegagalan parsing berupaParseErrorditanganiexpect()pada contoh kita karena teksnya memang tertulis benar. - Baris 11-14 (
let nama_bulan = [...]): daftar nama bulan Indonesia sebagai array 12 elemen — chrono bisa mencetak nama bulan, tetapi namanya mengikuti bahasa Inggris, jadi kami menyediakan sendiri namanya. - Baris 17-19 (
tanggal.day(),nama_bulan[(tanggal.month() - 1) as usize],tanggal.year()): traitDatelikemembuka komponen tanggal sebagai angka.month()bernilai 1 sampai 12, dikurangi satu menjadi indeks array;as usizemengubah tipe agar bisa dipakai sebagai indeks.
Ringkasan #
Crate rand dan chrono melengkapi dua wilayah yang sengaja ditinggalkan standard library. rng() memberi generator terunggulkan entropi sistem dengan inti algoritma ChaCha, random_range() menjamin distribusi seragam pada rentang apa pun, dan choose() mengambil satu elemen koleksi secara adil. Tipe-tipe chrono memisahkan tanggal murni (NaiveDate) dari waktu sadar zona (DateTime<Local>), dengan parse_from_str() dan format() yang berbagi penanda format strftime. Dengan lima crate inti — anyhow, thiserror, clap, rand, dan chrono — kotak perkakas kita sudah cukup untuk membangun aplikasi sungguhan; dua Chapter terakhir Section ini mengalihkan pandangan dari kode ke komunitas yang menumbuhkan seluruh ekosistem ini.