Python

πŸ¦€ Rust untuk Pemula: Panduan Lengkap

Tutorial lengkap belajar Rust dari nol β€” instalasi, variabel, ownership, borrowing, struct, enum, dan quiz interaktif dengan contoh kode praktis

1. Pengenalan Rust

Rust adalah bahasa pemrograman modern yang dikembangkan oleh Mozilla Research dan pertama kali dirilis secara stabil pada tahun 2015. Rust dirancang untuk memberikan keamanan memori (memory safety) tanpa garbage collector, sekaligus menawarkan performa yang sebanding dengan C dan C++.

Rust telah menjadi bahasa yang sangat populer dan secara konsisten dinobatkan sebagai "Bahasa Paling Dicintai" dalam survei Stack Overflow Developer Survey selama bertahun-tahun. Rust digunakan oleh perusahaan besar seperti Mozilla, Google, Microsoft, Amazon, dan Discord.

Mengapa Memilih Rust?

Keunggulan Penjelasan
Keamanan MemoriOwnership system mencegah bug memori seperti buffer overflow dan use-after-free saat compile time
Performa TinggiZero-cost abstractions β€” tidak ada overhead runtime, secepat C/C++
Concurrency AmanOwnership system mencegah data race secara otomatis
Modern ToolingCargo (package manager) yang powerful, built-in testing, dokumentasi
Cross-PlatformKompilasi untuk Windows, macOS, Linux, WebAssembly, dan embedded
Ekosistem Kayacrates.io menyediakan ribuan library siap pakai

Rust vs Bahasa Lain

Aspek Rust C++ Go
Memory Safety🟒 Compile-timeπŸ”΄ Manual🟒 Garbage Collector
Kecepatan🟒 Sangat Cepat🟒 Sangat Cepat🟑 Cepat
Concurrency🟒 Fearless🟑 Manual🟒 Goroutines
Learning CurveπŸ”΄ CuramπŸ”΄ Curam🟒 Mudah
Cocok untukSistem, Web, CLIGame, SistemCloud, Microservices
Diagram: Ekosistem Rust
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                   EKOSISTEM RUST                      β”‚
β”‚                                                       β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”‚
β”‚  β”‚ Web Dev  β”‚  β”‚ CLI Toolsβ”‚  β”‚  Sistem &        β”‚    β”‚
β”‚  β”‚ Actix    β”‚  β”‚ Clap     β”‚  β”‚  Embedded        β”‚    β”‚
β”‚  β”‚ Rocket   β”‚  β”‚ Serde    β”‚  β”‚  OS Kernel       β”‚    β”‚
β”‚  β”‚ Axum     β”‚  β”‚ Tokio    β”‚  β”‚  Driver          β”‚    β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β”‚
β”‚                                                       β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”‚
β”‚  β”‚ WebAssemblyβ”‚ β”‚ Game Dev β”‚  β”‚  Blockchain      β”‚    β”‚
β”‚  β”‚ wasm-pack β”‚  β”‚ Bevy     β”‚  β”‚  Solana          β”‚    β”‚
β”‚  β”‚ Yew       β”‚  β”‚ Amethyst β”‚  β”‚  Polkadot        β”‚    β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

2. Instalasi Rust

Rust menggunakan tool bernama rustup untuk menginstal dan mengelola berbagai versi Rust. Rustup juga menginstal Cargo, package manager bawaan Rust.

Instalasi di Linux dan macOS

Linux / macOS
# Instal Rust menggunakan rustup
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

# Ikuti instruksi di layar, pilih opsi 1 (default installation)

# Muat ulang environment
source $HOME/.cargo/env

# Verifikasi instalasi
rustc --version
# Output: rustc 1.78.0 (9b00956e5 2024-04-29)

cargo --version
# Output: cargo 1.78.0 (54d8815d0 2024-03-26)

Instalasi di Windows

Windows
# 1. Download rustup-init.exe dari https://rustup.rs
# 2. Jalankan rustup-init.exe
# 3. Ikuti instruksi instalasi (pilih opsi 1)
# 4. Pastikan Visual Studio C++ Build Tools terinstal

# Verifikasi di Command Prompt atau PowerShell:
rustc --version
# Output: rustc 1.78.0 (9b00956e5 2024-04-29)

cargo --version
# Output: cargo 1.78.0 (54d8815d0 2024-03-26)

# Update Rust ke versi terbaru:
rustup update

Menulis Program Rust Pertama

Rust β€” hello.rs
// File: hello.rs
fn main() {
    println!("Halo, dunia!");
    println!("Selamat datang di Rust!");
    println!("Nama saya: BeebaneLabs");
}

// Kompilasi dan jalankan dari terminal:
// rustc hello.rs
// ./hello
//
// Output:
// Halo, dunia!
// Selamat datang di Rust!
// Nama saya: BeebaneLabs

Menggunakan Cargo (Package Manager)

Cargo
# Buat proyek baru dengan Cargo
cargo new hello_rust
cd hello_rust

# Struktur proyek yang dihasilkan:
# hello_rust/
# β”œβ”€β”€ Cargo.toml    (konfigurasi proyek)
# └── src/
#     └── main.rs   (kode utama)

# Kompilasi dan jalankan
cargo run

# Hanya kompile tanpa menjalankan
cargo build

# Kompilasi dengan optimasi (release mode)
cargo build --release

# Cek apakah kode bisa dikompile tanpa error
cargo check

# Jalankan unit test
cargo test

# Generate dan buka dokumentasi
cargo doc --open

Cargo.toml β€” Konfigurasi Proyek

TOML β€” Cargo.toml
[package]
name = "hello_rust"
version = "0.1.0"
edition = "2021"

[dependencies]
# Tambahkan library dari crates.io di sini
# contoh: serde = "1.0"
# contoh: tokio = { version = "1", features = ["full"] }
πŸ’‘ Tips

Selalu gunakan Cargo untuk mengelola proyek Rust, bukan langsung menggunakan rustc. Cargo menangani kompilasi, dependency management, testing, dan banyak lagi. IDE yang direkomendasikan adalah VS Code dengan ekstensi rust-analyzer.

3. Variabel dan Tipe Data

Di Rust, variabel bersifat immutable (tidak bisa diubah) secara default. Ini adalah salah satu fitur unik Rust yang membantu mencegah bug. Anda harus menggunakan kata kunci mut secara eksplisit jika ingin mengubah nilai variabel.

Mendeklarasikan Variabel

Rust β€” Variabel
fn main() {
    // Variabel immutable (default, tidak bisa diubah)
    let nama = "Budi Santoso";
    let umur = 25;
    println!("Nama: {}, Umur: {}", nama, umur);

    // Variabel mutable (bisa diubah nilainya)
    let mut skor = 100;
    println!("Skor awal: {}", skor);   // 100
    skor = 150;
    println!("Skor baru: {}", skor);   // 150

    // Shadowing β€” mendeklarasi ulang variabel dengan nama sama
    let x = 5;
    let x = x + 1;        // x sekarang = 6
    let x = x * 2;        // x sekarang = 12
    println!("x = {}", x); // 12

    // Shadowing bisa mengubah tipe data
    let angka = "42";              // string
    let angka: i32 = angka.parse().unwrap();  // integer
    println!("angka = {}", angka); // 42

    // Deklarasi dengan tipe eksplisit
    let tinggi: f64 = 175.5;
    let aktif: bool = true;
    let huruf: char = 'R';

    println!("Tinggi: {}, Aktif: {}, Huruf: {}", tinggi, aktif, huruf);

    // Konstanta β€” selalu immutable, harus tipe eksplisit
    const PI: f64 = 3.14159265358979;
    const MAKS_PESERTA: u32 = 100;
    println!("PI = {}, Maks peserta = {}", PI, MAKS_PESERTA);
}

Tipe Data Dasar

Kategori Tipe Contoh Penjelasan
Integeri8, i16, i32, i64, i12842, -10Bilangan bulat (signed)
Integeru8, u16, u32, u64, u1280, 255Bilangan bulat (unsigned)
Floatf32, f643.14, -0.5Bilangan desimal
Booleanbooltrue, falseLogika benar-salah
Characterchar'A', 'πŸ¦€'Karakter Unicode (4 byte)
String&str, String"hello"Teks (string slice dan owned)
Tuple(T1, T2, ...)(1, "hi", true)Kumpulan tipe berbeda
Array[T; N][1, 2, 3]Kumpulan tipe sama, tetap ukuran

String vs &str

Rust β€” String
fn main() {
    // &str β€” string slice (immutable reference, fixed size)
    let s1: &str = "Hello, Rust!";

    // String β€” owned, bisa dimodifikasi
    let mut s2: String = String::from("Hello");
    s2.push_str(", World!");
    println!("{}", s2); // Hello, World!

    // Konversi &str β†’ String
    let s3: String = "hello".to_string();
    let s4: String = String::from("hello");

    // Konversi String β†’ &str (dereference)
    let s5: &str = &s3;
    let s6: &str = s3.as_str();

    // Format string
    let nama = "Budi";
    let umur = 25;
    let pesan = format!("Nama: {}, Umur: {}", nama, umur);
    println!("{}", pesan);

    // Multi-line string
    let teks = "\
Baris pertama
Baris kedua
Baris ketiga";
    println!("{}", teks);
}

Tuple dan Array

Rust β€” Tuple & Array
fn main() {
    // Tuple β€” kumpulan nilai dengan tipe berbeda
    let data: (i32, f64, &str) = (42, 3.14, "Rust");
    println!("Integer: {}, Float: {}, String: {}", data.0, data.1, data.2);

    // Destructuring tuple
    let (angka, desimal, teks) = data;
    println!("angka={}, desimal={}, teks={}", angka, desimal, teks);

    // Array β€” kumpulan nilai dengan tipe sama, ukuran tetap
    let angka = [1, 2, 3, 4, 5];
    println!("Elemen pertama: {}", angka[0]); // 1
    println!("Elemen terakhir: {}", angka[4]); // 5

    // Array dengan inisialisasi
    let nol = [0; 5]; // [0, 0, 0, 0, 0]
    println!("Array nol: {:?}", nol);

    // Panjang array
    println!("Panjang: {}", angka.len()); // 5

    // Iterasi array
    for item in angka.iter() {
        print!("{} ", item);
    }
    println!();
}

4. Fungsi dan Kontrol Alur

Fungsi di Rust didefinisikan dengan kata kunci fn. Rust memiliki aturan yang ketat tentang tipe data β€” semua parameter dan return value harus dideklarasikan secara eksplisit.

Mendefinisikan Fungsi

Rust β€” Fungsi
// Fungsi tanpa return value
fn sapa(nama: &str) {
    println!("Halo, {}! Selamat datang di Rust!", nama);
}

// Fungsi dengan return value (ekspresi terakhir tanpa semikolon)
fn tambah(a: i32, b: i32) -> i32 {
    a + b  // tanpa semikolon = return value
}

// Fungsi dengan return eksplisit
fn kali(a: i32, b: i32) -> i32 {
    return a * b;  // return eksplisit (kurang idiomatic)
}

// Fungsi dengan multiple return (menggunakan tuple)
fn bagi(a: f64, b: f64) -> (f64, f64) {
    let hasil = a / b;
    let sisa = a % b;
    (hasil, sisa)
}

fn main() {
    sapa("Budi");

    let hasil = tambah(10, 20);
    println!("10 + 20 = {}", hasil);

    let hasil_kali = kali(5, 6);
    println!("5 Γ— 6 = {}", hasil_kali);

    let (hasil_bagi, sisa) = bagi(17.0, 5.0);
    println!("17 / 5 = {} sisa {}", hasil_bagi, sisa);
}

Kontrol Alur: if-else dan match

Rust β€” Kontrol Alur
fn main() {
    // if-else
    let umur = 20;
    if umur >= 17 {
        println!("Dewasa");
    } else if umur >= 13 {
        println!("Remaja");
    } else {
        println!("Anak-anak");
    }

    // if sebagai ekspresi (bisa menghasilkan nilai)
    let status = if umur >= 18 { "Dewasa" } else { "Belum dewasa" };
    println!("Status: {}", status);

    // match β€” pattern matching (switch yang powerful)
    let hari = 3;
    let nama_hari = match hari {
        1 => "Senin",
        2 => "Selasa",
        3 => "Rabu",
        4 => "Kamis",
        5 => "Jumat",
        6 => "Sabtu",
        7 => "Minggu",
        _ => "Tidak valid",  // _ menangkap semua kemungkinan lain
    };
    println!("Hari ke-{} adalah {}", hari, nama_hari);

    // match dengan range
    let nilai = 85;
    let grade = match nilai {
        90..=100 => "A",
        80..=89 => "B",
        70..=79 => "C",
        60..=69 => "D",
        _ => "E",
    };
    println!("Nilai {} β†’ Grade {}", nilai, grade);
}

Loop dan Iterasi

Rust β€” Loop
fn main() {
    // loop β€” infinite loop
    let mut counter = 0;
    let hasil = loop {
        counter += 1;
        if counter == 5 {
            break counter * 2;  // break bisa mengembalikan nilai
        }
    };
    println!("Hasil loop: {}", hasil); // 10

    // while loop
    let mut angka = 5;
    while angka > 0 {
        print!("{}... ", angka);
        angka -= 1;
    }
    println!("Liftoff! πŸš€");

    // for loop (paling sering digunakan)
    for i in 1..=5 {
        print!("{} ", i);
    }
    println!(); // 1 2 3 4 5

    // Iterasi dengan enumerate
    let buah = ["apel", "mangga", "jeruk"];
    for (index, item) in buah.iter().enumerate() {
        println!("{}. {}", index + 1, item);
    }

    // Iterasi collection
    let angka = vec![10, 20, 30, 40, 50];
    let total: i32 = angka.iter().sum();
    println!("Total: {}", total); // 150
}

5. Ownership

Ownership adalah fitur paling unik dan penting di Rust. Sistem ownership memastikan keamanan memori tanpa memerlukan garbage collector. Setiap nilai di Rust memiliki satu owner (pemilik), dan ketika owner keluar dari scope, nilai tersebut otomatis di-drop (dihapus dari memori).

Tiga Aturan Ownership

⚠️ Aturan Ownership Rust
  1. Setiap nilai di Rust memiliki tepat satu owner
  2. Hanya bisa ada satu owner pada satu waktu
  3. Ketika owner keluar dari scope, nilai di-drop secara otomatis
Rust β€” Ownership
fn main() {
    // String di-allocate di heap, s1 adalah owner
    let s1 = String::from("Hello");

    // MOVE: ownership dipindahkan dari s1 ke s2
    let s2 = s1;
    // println!("{}", s1);  // ERROR! s1 sudah tidak valid

    println!("s2 = {}", s2); // OK, s2 adalah owner baru

    // CLONE: membuat deep copy (keduanya valid)
    let s3 = s2.clone();
    println!("s2 = {}, s3 = {}", s2, s3); // Keduanya valid

    // Tipe data yang disimpan di stack (Copy trait) tidak berpindah
    let x = 5;
    let y = x;  // COPY, bukan move
    println!("x = {}, y = {}", x, y); // Keduanya valid!

    // Ownership dan fungsi
    let nama = String::from("Budi");
    cetak_nama(nama);          // ownership dipindahkan ke fungsi
    // println!("{}", nama);   // ERROR! nama sudah tidak valid

    // Fungsi yang mengembalikan ownership
    let nama2 = String::from("Ani");
    let nama2 = ambil_dan_kembalikan(nama2);
    println!("Nama2: {}", nama2); // OK, ownership dikembalikan
}

fn cetak_nama(nama: String) {
    println!("Nama: {}", nama);
} // nama di-drop di sini

fn ambil_dan_kembalikan(nama: String) -> String {
    println!("Memproses: {}", nama);
    nama  // kembalikan ownership
}

6. Borrowing dan References

Alih-alih memindahkan ownership setiap kali, kita bisa meminjam (borrow) nilai menggunakan references. Ini memungkinkan Anda mengakses data tanpa mengambil ownership-nya.

Immutable References (&T)

Rust β€” Borrowing
// Fungsi yang meminjam string (tidak mengambil ownership)
fn hitung_panjang(s: &str) -> usize {
    s.len()
} // s di-drop di sini, tapi ownership tetap di pemanggil

fn main() {
    let s = String::from("Hello, Rust!");

    // &s membuat reference ke s, ownership TIDAK berpindah
    let panjang = hitung_panjang(&s);

    println!("\"{}\" panjangnya {} karakter", s, panjang); // OK!

    // Multiple immutable references β€” diizinkan!
    let r1 = &s;
    let r2 = &s;
    println!("r1={}, r2={}", r1, r2); // OK
}

Mutable References (&mut T)

Rust β€” Mutable Reference
fn tambah_excl(s: &mut String) {
    s.push_str("!");
}

fn main() {
    let mut pesan = String::from("Halo");

    // Mutable reference β€” bisa mengubah data asli
    tambah_excl(&mut pesan);
    println!("{}", pesan); // Halo!

    // Aturan: hanya SATU mutable reference pada satu waktu
    let r1 = &mut pesan;
    // let r2 = &mut pesan;  // ERROR! Tidak bisa dua mutable ref

    r1.push_str(" Dunia");
    println!("{}", r1); // Halo! Dunia

    // Tidak bisa gabungkan mutable dan immutable reference
    // pada scope yang sama
    let mut data = String::from("test");
    let r_immut = &data;
    // let r_mut = &mut data;  // ERROR!
    println!("{}", r_immut);
    // Setelah r_immut selesai digunakan, baru bisa mutable:
    let r_mut = &mut data;
    r_mut.push_str(" ok");
    println!("{}", r_mut);
}
πŸ’‘ Ringkasan Aturan References

Pada satu waktu, Anda bisa memiliki: satu mutable reference ATAU banyak immutable references, tapi tidak keduanya sekaligus. Referensi harus selalu valid (tidak boleh dangling reference).

7. Struct

Struct adalah tipe data kustom yang memungkinkan Anda mengelompokkan data terkait ke dalam satu kesatuan. Struct mirip dengan class di bahasa OOP, tetapi tanpa inheritance.

Mendefinisikan dan Menggunakan Struct

Rust β€” Struct
// Mendefinisikan struct
struct Mahasiswa {
    nama: String,
    umur: u32,
    ipk: f64,
    aktif: bool,
}

// Struct dengan method (implementasi)
impl Mahasiswa {
    // Constructor (associated function)
    fn baru(nama: &str, umur: u32) -> Self {
        Mahasiswa {
            nama: String::from(nama),
            umur,
            ipk: 0.0,
            aktif: true,
        }
    }

    // Method (mengambil &self)
    fn tampilkan(&self) {
        println!("{} ({} tahun, IPK: {:.2})", self.nama, self.umur, self.ipk);
    }

    // Method mutable (mengambil &mut self)
    fn tambah_ipk(&mut self, nilai: f64) {
        self.ipk = (self.ipk + nilai) / 2.0;
    }

    // Method yang mengambil ownership (jarang digunakan)
    fn lulus(self) -> String {
        format!("{} telah lulus!", self.nama)
    }
}

fn main() {
    // Membuat instance struct
    let mhs1 = Mahasiswa {
        nama: String::from("Budi"),
        umur: 21,
        ipk: 3.75,
        aktif: true,
    };

    println!("Nama: {}", mhs1.nama);
    println!("Umur: {}", mhs1.umur);

    // Mutable struct
    let mut mhs2 = Mahasiswa::baru("Ani", 20);
    mhs2.tambah_ipk(3.5);
    mhs2.tampilkan();

    // Struct update syntax
    let mhs3 = Mahasiswa {
        nama: String::from("Citra"),
        ipk: 3.90,
        ..mhs2  // sisanya dari mhs2
    };
    mhs3.tampilkan();

    // Tuple struct
    struct Warna(u8, u8, u8);
    let merah = Warna(255, 0, 0);
    println!("Warna: ({}, {}, {})", merah.0, merah.1, merah.2);

    // Unit struct
    struct Marker;
}

8. Enum dan Pattern Matching

Enum (enumeration) memungkinkan Anda mendefinisikan tipe yang bisa memiliki salah satu dari beberapa kemungkinan variant. Enum di Rust sangat powerful karena setiap variant bisa menyimpan data.

Mendefinisikan Enum

Rust β€” Enum
// Enum sederhana
enum Arah {
    Utara,
    Selatan,
    Timur,
    Barat,
}

// Enum dengan data
enum Pesan {
    Keluar,                                  // tanpa data
    Teks(String),                            // satu String
    Pindah { x: i32, y: i32 },              // named fields
    Warna(u8, u8, u8),                      // tuple fields
}

// Enum dengan method
impl Pesan {
    fn proses(&self) {
        match self {
            Pesan::Keluar => println!("Keluar dari aplikasi"),
            Pesan::Teks(teks) => println!("Pesan: {}", teks),
            Pesan::Pindah { x, y } => println!("Pindah ke ({}, {})", x, y),
            Pesan::Warna(r, g, b) => println!("Warna: #{:02X}{:02X}{:02X}", r, g, b),
        }
    }
}

fn main() {
    let arah = Arah::Utara;
    match arah {
        Arah::Utara => println!("Ke Utara ⬆"),
        Arah::Selatan => println!("Ke Selatan ⬇"),
        Arah::Timur => println!("Ke Timur ➑"),
        Arah::Barat => println!("Ke Barat β¬…"),
    }

    // Menggunakan enum dengan data
    let msg1 = Pesan::Teks(String::from("Halo Dunia"));
    let msg2 = Pesan::Pindah { x: 10, y: 20 };
    let msg3 = Pesan::Warna(255, 128, 0);

    msg1.proses(); // Pesan: Halo Dunia
    msg2.proses(); // Pindah ke (10, 20)
    msg3.proses(); // Warna: FF8000
}

Option<T> β€” Nullable Values

Rust β€” Option
// Option sudah built-in di Rust:
// enum Option<T> {
//     Some(T),
//     None,
// }

fn cari_index(data: &[i32], target: i32) -> Option<usize> {
    for (i, &item) in data.iter().enumerate() {
        if item == target {
            return Some(i);
        }
    }
    None
}

fn main() {
    let angka = vec![10, 20, 30, 40, 50];

    // Menggunakan match dengan Option
    match cari_index(&angka, 30) {
        Some(idx) => println!("Ditemukan di index {}", idx),
        None => println!("Tidak ditemukan"),
    }

    // if let β€” shorthand untuk match satu case
    if let Some(idx) = cari_index(&angka, 40) {
        println!("Index 40: {}", idx);
    }

    // unwrap_or β€” nilai default jika None
    let idx = cari_index(&angka, 99).unwrap_or(0);
    println!("Index: {}", idx); // 0

    // map β€” transformasi nilai di dalam Option
    let hasil = cari_index(&angka, 30).map(|i| i * 10);
    println!("Hasil: {:?}", hasil); // Some(200)
}

9. Collections

Rust menyediakan beberapa koleksi standar yang sangat berguna untuk menyimpan kumpulan data.

Vec<T> β€” Dynamic Array

Rust β€” Vec
fn main() {
    // Membuat vector
    let mut angka: Vec<i32> = Vec::new();
    angka.push(10);
    angka.push(20);
    angka.push(30);

    // Vec dengan macro
    let buah = vec!["apel", "mangga", "jeruk"];

    // Akses elemen
    println!("Buah pertama: {}", buah[0]); // apel

    // Akses aman dengan get (mengembalikan Option)
    match buah.get(5) {
        Some(item) => println!("Ada: {}", item),
        None => println!("Index tidak valid"),
    }

    // Iterasi
    for item in &buah {
        print!("{} ", item);
    }
    println!();

    // Iterasi mutable
    let mut skor = vec![80, 90, 70];
    for item in &mut skor {
        *item += 5; // tambah 5 ke setiap skor
    }
    println!("Skor: {:?}", skor); // [85, 95, 75]

    // Method berguna
    println!("Panjang: {}", angka.len());
    println!("Kosong? {}", angka.is_empty());
    println!("Contains 20? {}", angka.contains(&20));

    // Filter dan collect
    let besar: Vec<&i32> = angka.iter().filter(|&&x| x > 15).collect();
    println!("> 15: {:?}", besar); // [20, 30]
}

HashMap<K, V>

Rust β€” HashMap
use std::collections::HashMap;

fn main() {
    // Membuat HashMap
    let mut nilai: HashMap<&str, i32> = HashMap::new();

    // Insert data
    nilai.insert("Matematika", 85);
    nilai.insert("Fisika", 90);
    nilai.insert("Kimia", 78);

    // Akses data
    if let Some(n) = nilai.get("Matematika") {
        println!("Nilai Matematika: {}", n);
    }

    // Iterasi
    for (mapel, n) in &nilai {
        println!("{}: {}", mapel, n);
    }

    // Update: overwrite jika sudah ada
    nilai.insert("Matematika", 95);

    // Entry API: insert jika belum ada
    nilai.entry("Biologi").or_insert(88);
    nilai.entry("Matematika").or_insert(50); // tidak berubah

    // Counter pattern
    let teks = "halo halo dunia halo";
    let mut hitungan: HashMap<&str, i32> = HashMap::new();
    for kata in teks.split_whitespace() {
        let count = hitungan.entry(kata).or_insert(0);
        *count += 1;
    }
    println!("Hitungan kata: {:?}", hitungan);
}

10. Error Handling Dasar

Rust menangani error secara eksplisit menggunakan tipe Result<T, E> dan Option<T>, bukan dengan exception seperti bahasa lain.

Rust β€” Error Handling
use std::fs;
use std::num::ParseIntError;

// Fungsi yang bisa gagal β€” mengembalikan Result
fn bagi(a: f64, b: f64) -> Result<f64, String> {
    if b == 0.0 {
        Err(String::from("Tidak bisa bagi dengan nol!"))
    } else {
        Ok(a / b)
    }
}

// Fungsi dengan ? operator (propagate error)
fn baca_angka_dari_file(path: &str) -> Result<i32, Box<dyn std::error::Error>> {
    let konten = fs::read_to_string(path)?;  // ? propagates error
    let angka: i32 = konten.trim().parse()?; // ? propagates error
    Ok(angka)
}

fn main() {
    // Menggunakan Result dengan match
    match bagi(10.0, 3.0) {
        Ok(hasil) => println!("10 / 3 = {:.2}", hasil),
        Err(e) => println!("Error: {}", e),
    }

    match bagi(10.0, 0.0) {
        Ok(hasil) => println!("Hasil: {}", hasil),
        Err(e) => println!("Error: {}", e),
    }

    // unwrap β€” langsung ambil nilai, panic jika Err
    let hasil = bagi(10.0, 2.0).unwrap(); // OK: 5.0
    // let gagal = bagi(10.0, 0.0).unwrap(); // PANIC!

    // unwrap_or β€” nilai default
    let hasil = bagi(10.0, 0.0).unwrap_or(0.0);
    println!("Hasil: {}", hasil); // 0.0

    // expect β€” panic dengan pesan custom
    let hasil = bagi(10.0, 2.0).expect("Pembagian gagal");

    // unwrap_or_else β€” fallback dengan closure
    let hasil = bagi(10.0, 0.0).unwrap_or_else(|e| {
        println!("Warning: {}", e);
        0.0
    });
    println!("Hasil: {}", hasil);
}

πŸ“ Quiz Pemahaman Rust Pemula

Uji pemahaman Anda tentang materi Rust di atas! Pilih jawaban yang paling tepat.

1. Apa yang terjadi saat kita melakukan let s2 = s1; untuk tipe String?

a) s1 dan s2 keduanya valid dan menunjuk ke data yang sama
b) Ownership s1 dipindahkan ke s2, s1 tidak lagi valid
c) Data di-clone secara otomatis
d) Terjadi error saat compile

2. Berapa banyak mutable reference yang bisa ada pada satu waktu?

a) Tidak terbatas
b) Hanya satu
c) Dua
d) Tergantung tipe datanya

3. Apa fungsi kata kunci mut pada deklarasi variabel?

a) Menghapus variabel dari memori
b) Mengonversi tipe data variabel
c) Menandai variabel sebagai mutable (bisa diubah)
d) Membuat variabel menjadi konstanta

4. Apa output dari kode berikut?
let x = 5;
let x = x + 1;
let x = x * 2;
println!("{}", x);

a) 5
b) 6
c) 12
d) Error

5. Apa yang dilakukan operator ? pada fungsi yang mengembalikan Result?

a) Mengabaikan error dan melanjutkan eksekusi
b) Memanggil unwrap() secara otomatis
c) Meng-propagate error ke pemanggil fungsi
d) Mengonversi error menjadi None
πŸ” Zoom
100%
🎨 Tema