Rust sudah bertahun-tahun memuncaki daftar bahasa yang paling disukai developer di survei Stack Overflow. Alasannya bukan sekadar kecepatan — C juga cepat — tapi karena kompilernya menolak kode yang berpotensi bug sebelum program sempat dijalankan. Kalau kamu datang dari Python atau JavaScript, tutorial ini terasa agak keras di awal. Itu bukan kebetulan. Kita akan pasang toolchain-nya, memahami ownership yang bikin banyak orang menyerah di hari pertama, lalu langsung membangun tool command-line yang benar-benar berguna.
Kenapa Rust, dan Apa Bedanya dengan Go?
Kalau kamu sudah pernah belajar Go, kamu akan cepat menangkap posisi Rust. Go unggul di kesederhanaan dan kecepatan compile. Rust unggul di kontrol penuh atas memori tanpa garbage collector. Tidak ada null yang bikin panik, tidak ada data race yang lolos compile, dan tidak ada segfault misterius di production. Bayarannya satu: kamu harus paham ownership dulu, dan pesan error kompilernya kadang sepanjang satu layar.
Instalasi dengan rustup
rustup adalah installer resmi yang sekaligus mengelola versi toolchain, mirip nvm di Node.js. Di Linux atau macOS, jalankan satu baris ini lalu ikuti perintahnya:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
Pilih opsi default — profile minimal atau complete sama-sama cukup untuk tutorial ini. Setelah selesai, muat ulang environment dan pastikan semuanya terpasang:
source "$HOME/.cargo/env"
rustc --version
cargo --version
Proyek Pertama dengan Cargo
cargo adalah package manager sekaligus build tool bawaan Rust. Fungsinya seperti kombinasi pip dan venv, tapi jauh lebih ketat soal versi. Buat proyek baru:
cargo new penghitung_kata
cd penghitung_kata
Cargo membuat dua hal penting: Cargo.toml untuk metadata dan dependensi, serta src/main.rs untuk kode. Jalankan template bawaannya:
cargo run
Kamu akan melihat Hello, world!. cargo run meng-compile lalu mengeksekusi sekaligus. Mau compile saja tanpa jalan? Pakai cargo build. Mau cek error tanpa menghasilkan binary? cargo check — ini yang paling sering saya pakai karena jauh lebih cepat.
Variabel, Mutability, dan Shadowing
Di Rust semua variabel immutable secara default. Kebalikan dari Python atau JavaScript, dan itulah yang membuat kode Rust terasa aman — kamu harus eksplisit kalau mau mengubah nilai.
fn main() {
let x = 5;
println!("nilai x: {x}");
// x = 6; // error: cannot assign twice to immutable variable
let mut y = 10;
y += 5;
println!("nilai y: {y}");
// shadowing: deklarasi ulang nama yang sama, tipe boleh berubah
let z = 5;
println!("z = {z}");
let z = z + 1;
println!("z = {z}");
let z = "sekarang jadi string";
println!("z = {z}");
}
Ownership dan Borrowing: Inti dari Rust
Inilah konsep yang paling sering bikin orang berhenti belajar Rust. Aturannya cuma tiga: setiap nilai punya satu pemilik; nilai bisa dipindahkan atau dipinjam; dan saat pemilik keluar dari scope, nilainya langsung dibersihkan. Tanpa garbage collector.
fn main() {
let s1 = String::from("bengkel");
let s2 = s1; // ownership pindah ke s2, s1 tidak valid lagi
// println!("{s1}"); // error: borrow of moved value
println!("{s2}");
let s3 = String::from("kode");
let panjang = hitung_panjang(&s3); // pinjam, bukan pindah
println!("panjang {s3} = {panjang}");
}
fn hitung_panjang(s: &String) -> usize {
s.len()
}
Tanda &s3 berarti meminjam tanpa mengambil alih. Setelah fungsi selesai, s3 tetap bisa dipakai. Hapus tanda &-nya dan kompiler akan protes karena s3 sudah berpindah ke dalam fungsi.
Borrow checker adalah fitur, bukan musuh. Ketika compile gagal, baca pesan errornya — Rust biasanya memberi tahu persis baris mana yang bermasalah dan bahkan menyarankan perbaikan, misalnya menambahkan & atau .clone().
Struct, Enum, dan Error Handling
Rust tidak punya exception seperti try/except. Fungsi yang bisa gagal mengembalikan Result<T, E> atau Option<T>, dan kamu wajib menanganinya. Tidak ada error yang terlewat diam-diam.
struct Kata {
teks: String,
jumlah: usize,
}
fn bagi(a: f64, b: f64) -> Result<f64, String> {
if b == 0.0 {
Err(String::from("tidak bisa membagi dengan nol"))
} else {
Ok(a / b)
}
}
fn main() {
let w = Kata {
teks: String::from("rust"),
jumlah: 1,
};
println!("{} muncul {} kali", w.teks, w.jumlah);
match bagi(10.0, 2.0) {
Ok(hasil) => println!("hasil: {hasil}"),
Err(e) => println!("error: {e}"),
}
}
Membangun CLI: Penghitung Frekuensi Kata
Sekarang gabungkan semuanya jadi satu tool nyata. Tool ini membaca file teks, menghitung kemunculan tiap kata, lalu mencetak hasilnya terurut alfabetis. Kita cuma pakai standard library, belum perlu dependensi eksternal.
use std::collections::BTreeMap;
use std::env;
use std::fs;
fn main() {
let args: Vec<String> = env::args().collect();
if args.len() < 2 {
eprintln!("Cara pakai: cargo run -- <nama_file>");
std::process::exit(1);
}
let path = &args[1];
let isi = match fs::read_to_string(path) {
Ok(teks) => teks,
Err(e) => {
eprintln!("Gagal membaca {path}: {e}");
std::process::exit(1);
}
};
let mut frekuensi: BTreeMap<String, usize> = BTreeMap::new();
for kata in isi.split_whitespace() {
let bersih: String = kata
.chars()
.filter(|c| c.is_alphanumeric())
.collect::<String>()
.to_lowercase();
if bersih.is_empty() {
continue;
}
*frekuensi.entry(bersih).or_insert(0) += 1;
}
println!("Total kata unik: {}", frekuensi.len());
let total: usize = frekuensi.values().sum();
println!("Total kata: {total}\n");
for (kata, jumlah) in frekuensi.iter() {
println!("{kata:15} {jumlah}");
}
}
BTreeMap menyimpan pasangan kata-jumlah dalam urutan terurut, jadi hasilnya otomatis alfabetis. entry().or_insert(0) menggantikan blok if kata in dict yang biasa kamu tulis di Python, sekaligus menghindari dua lookup untuk satu operasi.
Baris *frekuensi.entry(bersih).or_insert(0) += 1 terlihat aneh di awal. Cara bacanya: ambil slot untuk kata ini, kalau belum ada isi 0, lalu tambah 1. Tanda * dipakai karena entry mengembalikan referensi yang harus di-dereference sebelum mengubah nilainya.
Menjalankan dan Menguji
Buat file teks contoh, lalu jalankan tool-nya:
printf 'rust cepat dan aman rust memang cepat\n' > contoh.txt
cargo run -- contoh.txt
Hasilnya kira-kira seperti ini:
Total kata unik: 5
Total kata: 7
aman 1
cepat 2
dan 1
memang 1
rust 2
Langkah Selanjutnya
Tool ini baru permukaan. Kalau kamu mau CLI yang lebih serius — dengan flag --help otomatis dan validasi argumen — konsepnya sama seperti yang saya tulis di tutorial Typer untuk Python, dan di Rust ekosistemnya diwakili crate clap. Untuk memecah teks yang lebih pintar daripada split_whitespace, kamu bisa memakai regular expression. Dari sini, coba tambahkan fitur: baca banyak file sekaligus, hitung baris dan karakter seperti wc, lalu bungkus dengan clap. Referensi paling lengkap tetap The Rust Book di doc.rust-lang.org/book — baca bab ownership-nya dua kali kalau perlu.