Sintaks Utama

Sintaks Utama #

Rust dirancang dengan prinsip bahwa keamanan memori dan kinerja tinggi bukan dua hal yang harus dikompromikan. Konsekuensinya, sintaks Rust terasa berbeda dari bahasa seperti Python, Java, atau Go — ada konsep ownership, borrowing, dan lifetime yang tidak ada padanannya di tempat lain. Artikel ini memberikan gambaran menyeluruh tentang elemen-elemen sintaks Rust yang akan kamu temui setiap hari: dari cara mendeklarasikan variabel yang immutable secara default, hingga bagaimana match menggantikan switch dengan cara yang jauh lebih ekspresif. Pemahaman terhadap fondasi ini adalah prasyarat sebelum masuk ke topik-topik yang lebih dalam di artikel-artikel berikutnya.

Anatomi Program Rust #

Setiap program Rust yang bisa dijalankan memiliki satu titik masuk: fungsi main. Memahami strukturnya dari awal membantu kamu membaca kode Rust orang lain dengan lebih cepat.

// Ini adalah program Rust paling sederhana yang bisa dijalankan

fn main() {
    // println! adalah macro, bukan fungsi biasa
    // Tanda seru (!) membedakan macro dari function call
    println!("Halo, Rust!");

    // Deklarasi variabel menggunakan let
    let angka = 42;
    let pesan = "Nilai angka adalah";

    // Format string menggunakan {} sebagai placeholder
    println!("{}: {}", pesan, angka);
}

Beberapa hal yang langsung terlihat berbeda dari bahasa lain:

  • Tanda ! setelah nama fungsi menandakan itu adalah macro, bukan function call biasa. println!, vec!, format! semuanya adalah macro.
  • Setiap statement diakhiri dengan titik koma (;), kecuali expression yang menjadi return value.
  • Rust adalah bahasa yang statically typed — tipe semua variabel diketahui saat kompilasi, meski seringkali bisa diinfer secara otomatis oleh compiler.

Komentar #

Rust mendukung tiga gaya komentar yang masing-masing punya tujuan berbeda. Perbedaan antara komentar biasa dan komentar dokumentasi bukan sekadar gaya — komentar dokumentasi benar-benar diproses oleh tool rustdoc untuk menghasilkan dokumentasi HTML.

// Komentar satu baris — diabaikan oleh compiler

/*
   Komentar blok — bisa mencakup
   beberapa baris sekaligus
*/

/// Komentar dokumentasi untuk item di bawahnya (fungsi, struct, dll.)
/// Mendukung sintaks Markdown dan diproses oleh rustdoc.
///
/// # Contoh
///
/// ```
/// let hasil = tambah(2, 3);
/// assert_eq!(hasil, 5);
/// ```
fn tambah(a: i32, b: i32) -> i32 {
    a + b
}

//! Komentar dokumentasi untuk modul atau crate itu sendiri
//! Biasanya ada di baris pertama file lib.rs atau main.rs

Komentar /// memiliki fitur unik: blok kode di dalamnya (antara triple backtick) bisa dijalankan sebagai doc test oleh cargo test. Ini memastikan contoh kode di dokumentasi selalu sinkron dengan implementasi aktual.


Variabel dan Mutabilitas #

Variabel di Rust immutable secara default. Ini bukan sekadar konvensi — compiler akan menolak kompilasi jika kamu mencoba mengubah nilai variabel yang tidak dideklarasikan mut. Keputusan desain ini mendorong kamu untuk berpikir eksplisit tentang mana data yang perlu berubah dan mana yang tidak.

fn main() {
    // ANTI-PATTERN: mencoba mengubah variabel immutable
    let x = 5;
    x = 6; // error[E0384]: cannot assign twice to immutable variable `x`

    // BENAR: deklarasikan mut jika memang perlu diubah
    let mut y = 5;
    y = 6; // ✓ valid
    println!("y = {}", y);
}

Shadowing #

Rust memiliki konsep shadowing — kamu bisa mendeklarasikan variabel baru dengan nama yang sama menggunakan let lagi. Ini berbeda dari mutasi: variabel lama digantikan oleh variabel baru yang bisa bertipe berbeda.

fn main() {
    let angka = 5;

    // Shadowing: buat variabel baru dengan nama sama
    let angka = angka + 1;       // angka = 6

    // Shadowing bahkan bisa mengubah tipe
    let angka = angka.to_string(); // sekarang angka bertipe String, bukan i32

    println!("angka = {}", angka); // angka = "6"

    // ANTI-PATTERN: menggunakan mut untuk "mengubah tipe"
    // let mut nilai = 5;
    // nilai = "lima"; // error — tidak bisa mengubah tipe variabel mut
}

Konstanta #

Konstanta dideklarasikan dengan const, bukan let. Berbeda dari variabel immutable, konstanta harus selalu memiliki anotasi tipe eksplisit, nilainya harus diketahui saat kompilasi, dan berlaku di seluruh scope.

// Konstanta menggunakan SCREAMING_SNAKE_CASE
const KECEPATAN_CAHAYA: u64 = 299_792_458; // m/s

// Underscore bisa digunakan sebagai pemisah digit untuk keterbacaan
const POPULASI_BUMI: u64 = 8_000_000_000;

fn main() {
    println!("Kecepatan cahaya: {} m/s", KECEPATAN_CAHAYA);
}

Tipe Data #

Rust memiliki sistem tipe yang kaya. Compiler menginfer tipe secara otomatis dalam banyak kasus, tapi memahami tipe-tipe dasarnya penting untuk menulis kode yang benar dan efisien.

Tipe Skalar #

Tipe skalar merepresentasikan satu nilai tunggal. Ada empat kategori utama:

KategoriTipeUkuranRentang / Keterangan
Integer signedi81 byte-128 hingga 127
i162 byte-32.768 hingga 32.767
i324 byteDefault untuk integer
i648 byteLebih dari 9 kuadriliun
i12816 byteSangat besar
isizePlatform32 atau 64 bit
Integer unsignedu81 byte0 hingga 255
u162 byte0 hingga 65.535
u324 byte0 hingga ~4,3 miliar
u648 byte0 hingga ~18,4 kuintiliun
u12816 byteSangat besar
usizePlatformDigunakan untuk indeks
Floatf324 bytePresisi tunggal
f648 bytePresisi ganda (default)
Booleanbool1 bytetrue atau false
Karakterchar4 byteUnicode scalar value
fn main() {
    // Integer — gunakan suffix untuk tipe eksplisit
    let a: i32 = -42;
    let b = 1_000_000u64;    // suffix u64, underscore untuk keterbacaan
    let c = 0xFF;            // hexadecimal
    let d = 0b1111_0000;     // binary
    let e = 0o77;            // octal

    // Float
    let f: f64 = 3.14159;
    let g = 2.0f32;          // suffix f32

    // Boolean
    let benar: bool = true;
    let salah = false;

    // Char — gunakan single quote, bukan double quote
    let huruf = 'A';
    let emoji = '🦀'; // Rust char mendukung semua Unicode
    let karakter_cina = '中';

    println!("{} {} {} {} {}", a, f, benar, huruf, emoji);
}

Tipe Compound #

Tipe compound mengelompokkan beberapa nilai menjadi satu.

fn main() {
    // Tuple: kumpulan nilai dengan tipe yang bisa berbeda-beda
    let koordinat: (f64, f64, f64) = (1.0, 2.5, -0.3);

    // Akses via indeks
    let x = koordinat.0;
    let y = koordinat.1;

    // Destructuring — cara yang lebih idiomatis
    let (px, py, pz) = koordinat;
    println!("Posisi: {}, {}, {}", px, py, pz);

    // Array: kumpulan nilai dengan tipe SAMA dan ukuran TETAP
    let angka: [i32; 5] = [1, 2, 3, 4, 5];
    let semua_nol = [0; 10]; // 10 elemen, semuanya 0

    // Akses via indeks
    println!("Elemen pertama: {}", angka[0]);
    println!("Panjang array: {}", angka.len());

    // ANTI-PATTERN: akses indeks di luar batas
    // let x = angka[10]; // runtime panic: index out of bounds
}
Array di Rust berbeda dari Vec<T>. Array berukuran tetap dan dialokasikan di stack, sementara Vec<T> berukuran dinamis dan dialokasikan di heap. Untuk koleksi yang ukurannya tidak diketahui saat kompilasi, gunakan Vec<T>.

Kontrol Alur #

If dan If-Let #

if di Rust adalah expression, bukan statement. Artinya, ia bisa mengembalikan nilai dan digunakan di sisi kanan assignment.

fn main() {
    let suhu = 28;

    // if sebagai statement biasa
    if suhu > 30 {
        println!("Panas");
    } else if suhu > 20 {
        println!("Nyaman");
    } else {
        println!("Dingin");
    }

    // BENAR: if sebagai expression — mengembalikan nilai
    let kondisi = if suhu > 30 { "panas" } else { "sejuk" };
    println!("Cuaca {}", kondisi);

    // ANTI-PATTERN: tipe pada setiap branch harus sama
    // let kondisi = if suhu > 30 { "panas" } else { 0 };
    // error: `if` and `else` have incompatible types
}

Match #

match adalah salah satu fitur paling kuat di Rust. Ia memaksa kamu menangani semua kemungkinan — compiler akan error jika ada case yang tidak ditangani.

fn main() {
    let angka = 7;

    // ANTI-PATTERN: menggunakan if-else berantai untuk pattern matching
    // if angka == 1 { ... } else if angka == 2 { ... } — verbose dan tidak exhaustive

    // BENAR: gunakan match
    match angka {
        1 => println!("Satu"),
        2 | 3 => println!("Dua atau Tiga"),      // multiple pattern
        4..=6 => println!("Empat sampai Enam"),   // range pattern
        n if n > 6 => println!("Lebih dari 6: {}", n), // guard condition
        _ => println!("Lainnya"),                  // wildcard — wajib jika tidak exhaustive
    }

    // match juga bisa mengembalikan nilai
    let deskripsi = match angka {
        1..=3 => "kecil",
        4..=6 => "sedang",
        _ => "besar",
    };
    println!("Angka {}: {}", angka, deskripsi);
}

Loop, While, dan For #

Rust memiliki tiga konstruksi perulangan, masing-masing untuk kasus yang berbeda.

fn main() {
    // loop — perulangan tanpa batas, dihentikan dengan break
    // Bisa mengembalikan nilai via break
    let mut counter = 0;
    let hasil = loop {
        counter += 1;
        if counter == 10 {
            break counter * 2; // kembalikan nilai dari loop
        }
    };
    println!("Hasil loop: {}", hasil); // 20

    // while — perulangan selama kondisi true
    let mut n = 0;
    while n < 5 {
        print!("{} ", n);
        n += 1;
    }
    println!();

    // for — cara paling idiomatis untuk iterasi koleksi
    let koleksi = [10, 20, 30, 40, 50];
    for elemen in koleksi {
        print!("{} ", elemen);
    }
    println!();

    // Range: 0..5 (eksklusif), 0..=5 (inklusif)
    for i in 0..5 {
        print!("{} ", i); // 0 1 2 3 4
    }
    println!();

    // Enumerate untuk mendapat indeks sekaligus nilai
    for (indeks, nilai) in koleksi.iter().enumerate() {
        println!("[{}] = {}", indeks, nilai);
    }
}

Fungsi #

Fungsi di Rust dideklarasikan dengan fn. Parameter selalu membutuhkan anotasi tipe eksplisit — tidak ada type inference untuk parameter fungsi.

// Fungsi tanpa return value (return type () — unit type)
fn sapa(nama: &str) {
    println!("Halo, {}!", nama);
}

// Fungsi dengan return value — tipe setelah ->
fn tambah(a: i32, b: i32) -> i32 {
    // BENAR: expression tanpa titik koma = return value implisit
    a + b
}

// ANTI-PATTERN: menggunakan return eksplisit untuk early return yang bukan early return
fn kurang(a: i32, b: i32) -> i32 {
    return a - b; // berlebihan untuk kasus sederhana
}

// BENAR: return eksplisit hanya untuk early return
fn pembagian_aman(a: i32, b: i32) -> Option<i32> {
    if b == 0 {
        return None; // early return — di sini return eksplisit masuk akal
    }
    Some(a / b) // return implisit di akhir fungsi
}

fn main() {
    sapa("Rustacean");
    println!("3 + 4 = {}", tambah(3, 4));

    match pembagian_aman(10, 0) {
        Some(hasil) => println!("Hasil: {}", hasil),
        None => println!("Tidak bisa dibagi nol"),
    }
}

Closure #

Closure adalah fungsi anonim yang bisa menangkap variabel dari scope di sekitarnya.

fn main() {
    let pengali = 3;

    // Closure menangkap `pengali` dari scope luar
    let kalikan = |x: i32| x * pengali;

    println!("5 x {} = {}", pengali, kalikan(5)); // 15

    // Closure sering digunakan dengan iterator methods
    let angka = vec![1, 2, 3, 4, 5];

    let genap: Vec<i32> = angka.iter()
        .filter(|&&x| x % 2 == 0)
        .copied()
        .collect();

    let dikuadratkan: Vec<i32> = angka.iter()
        .map(|&x| x * x)
        .collect();

    println!("Genap: {:?}", genap);           // [2, 4]
    println!("Kuadrat: {:?}", dikuadratkan);  // [1, 4, 9, 16, 25]
}

Struct #

Struct adalah cara Rust untuk membuat tipe data kustom dengan field bernama. Ini setara dengan class di bahasa lain, tapi tanpa inheritance.

// Definisi struct
struct Persegi {
    lebar: u32,
    tinggi: u32,
}

// Implementasi method untuk struct menggunakan impl block
impl Persegi {
    // Associated function (bukan method) — tidak ada parameter self
    // Biasanya digunakan sebagai constructor
    fn baru(lebar: u32, tinggi: u32) -> Persegi {
        Persegi { lebar, tinggi } // field shorthand jika nama sama dengan variabel
    }

    // Method — parameter pertama selalu &self, &mut self, atau self
    fn luas(&self) -> u32 {
        self.lebar * self.tinggi
    }

    fn keliling(&self) -> u32 {
        2 * (self.lebar + self.tinggi)
    }

    fn perbesar(&mut self, faktor: u32) {
        self.lebar *= faktor;
        self.tinggi *= faktor;
    }
}

fn main() {
    let mut p = Persegi::baru(10, 5);
    println!("Luas: {}", p.luas());       // 50
    println!("Keliling: {}", p.keliling()); // 30

    p.perbesar(2);
    println!("Setelah diperbesar — Luas: {}", p.luas()); // 200

    // Struct update syntax — salin field dari struct lain
    let p2 = Persegi { lebar: 20, ..p };
    println!("p2 — Lebar: {}, Tinggi: {}", p2.lebar, p2.tinggi); // 20, 10
}

Enum #

Enum di Rust jauh lebih ekspresif dibanding enum di bahasa lain — setiap variant bisa membawa data dengan tipe yang berbeda.

// Enum dengan berbagai jenis variant
enum Pesan {
    Keluar,                          // variant tanpa data
    Pindah { x: i32, y: i32 },      // variant dengan named fields
    Tulis(String),                   // variant dengan satu nilai
    GantiWarna(u8, u8, u8),         // variant dengan beberapa nilai
}

impl Pesan {
    fn proses(&self) {
        match self {
            Pesan::Keluar => println!("Program keluar"),
            Pesan::Pindah { x, y } => println!("Pindah ke ({}, {})", x, y),
            Pesan::Tulis(teks) => println!("Menulis: {}", teks),
            Pesan::GantiWarna(r, g, b) => println!("Warna: #{:02X}{:02X}{:02X}", r, g, b),
        }
    }
}

fn main() {
    let pesan_list = vec![
        Pesan::Pindah { x: 10, y: 20 },
        Pesan::Tulis(String::from("Halo")),
        Pesan::GantiWarna(255, 128, 0),
        Pesan::Keluar,
    ];

    for pesan in &pesan_list {
        pesan.proses();
    }
}

Option dan Result — Dua Enum Terpenting #

Option<T> dan Result<T, E> adalah enum bawaan Rust yang menjadi fondasi error handling dan nilai opsional. Rust tidak memiliki nullOption<T> adalah penggantinya yang aman.

// Option<T> — nilai yang mungkin ada atau tidak ada
// enum Option<T> {
//     Some(T),
//     None,
// }

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

// Result<T, E> — operasi yang bisa berhasil atau gagal
// enum Result<T, E> {
//     Ok(T),
//     Err(E),
// }

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 data = [3, 7, 1, 9, 4];

    // Menangani Option
    match cari_elemen(&data, 9) {
        Some(indeks) => println!("Ditemukan di indeks {}", indeks),
        None => println!("Tidak ditemukan"),
    }

    // Cara ringkas dengan if let
    if let Some(idx) = cari_elemen(&data, 7) {
        println!("7 ada di indeks {}", idx);
    }

    // Menangani Result
    match bagi(10.0, 3.0) {
        Ok(hasil) => println!("Hasil: {:.4}", hasil),
        Err(e) => println!("Error: {}", e),
    }

    // Operator ? — propagasi error secara otomatis (dalam fungsi yang return Result)
    // Dibahas lebih detail di artikel Error Handling
}

Trait #

Trait mendefinisikan perilaku yang bisa dibagikan antar tipe. Konsepnya mirip dengan interface di Java atau Go, tapi lebih fleksibel — kamu bisa mengimplementasikan trait untuk tipe yang kamu buat maupun tipe yang sudah ada di library lain.

// Definisi trait
trait Deskripsi {
    // Method yang harus diimplementasikan
    fn deskripsikan(&self) -> String;

    // Method dengan implementasi default — bisa di-override
    fn cetak(&self) {
        println!("{}", self.deskripsikan());
    }
}

struct Pengguna {
    nama: String,
    email: String,
}

struct Produk {
    nama: String,
    harga: f64,
}

// Implementasi trait untuk Pengguna
impl Deskripsi for Pengguna {
    fn deskripsikan(&self) -> String {
        format!("Pengguna: {} ({})", self.nama, self.email)
    }
}

// Implementasi trait untuk Produk
impl Deskripsi for Produk {
    fn deskripsikan(&self) -> String {
        format!("Produk: {} — Rp{:.0}", self.nama, self.harga)
    }

    // Override implementasi default
    fn cetak(&self) {
        println!("=== {} ===", self.deskripsikan());
    }
}

// Trait sebagai parameter fungsi — generic dengan trait bound
fn tampilkan<T: Deskripsi>(item: &T) {
    item.cetak();
}

fn main() {
    let user = Pengguna {
        nama: String::from("Budi"),
        email: String::from("[email protected]"),
    };

    let produk = Produk {
        nama: String::from("Laptop"),
        harga: 15_000_000.0,
    };

    tampilkan(&user);
    tampilkan(&produk);
}

Ownership, Borrowing, dan Lifetime #

Ini adalah konsep yang paling unik di Rust — tidak ada di bahasa lain yang umum digunakan. Ownership adalah mekanisme yang memungkinkan Rust menjamin keamanan memori tanpa garbage collector.

flowchart TD
    A[Nilai dibuat] --> B{Siapa pemiliknya?}
    B --> C[Satu owner pada satu waktu]
    C --> D{Apa yang terjadi?}
    D --> E["Move — ownership berpindah\nowner lama tidak bisa digunakan"]
    D --> F["Borrow &T — pinjam read-only\nbisa ada banyak bersamaan"]
    D --> G["Borrow &mut T — pinjam read-write\nhanya satu pada satu waktu"]
    D --> H["Clone — buat salinan\nowner lama tetap valid"]
    E --> I[Owner baru bertanggung jawab atas drop]
    F --> I
    G --> I
    H --> I
    I --> J[Nilai di-drop otomatis saat owner keluar scope]

Ownership #

fn main() {
    // String dialokasikan di heap — ownership bisa dipindah
    let s1 = String::from("halo");
    let s2 = s1; // MOVE: s1 tidak bisa digunakan lagi

    // ANTI-PATTERN: menggunakan variabel setelah di-move
    // println!("{}", s1); // error[E0382]: borrow of moved value: `s1`

    // BENAR: gunakan s2 atau clone s1 sebelum di-move
    println!("{}", s2);

    let s3 = String::from("dunia");
    let s4 = s3.clone(); // clone: buat salinan, keduanya valid
    println!("{} dan {}", s3, s4);

    // Tipe Copy (integer, float, bool, char) tidak di-move — selalu di-copy
    let x = 5;
    let y = x;           // copy, bukan move
    println!("{} {}", x, y); // keduanya valid
}

Borrowing #

Borrowing memungkinkan kamu menggunakan nilai tanpa mengambil ownership-nya.

fn hitung_panjang(s: &String) -> usize { // &String = borrow, bukan take ownership
    s.len()
} // s keluar scope, tapi tidak di-drop karena hanya borrowed

fn tambahkan_kata(s: &mut String) { // &mut = mutable borrow
    s.push_str(", dunia");
}

fn main() {
    let s = String::from("halo");

    // Borrow immutable — s masih valid setelah pemanggilan
    let panjang = hitung_panjang(&s);
    println!("Panjang '{}' adalah {}", s, panjang);

    let mut s2 = String::from("halo");
    tambahkan_kata(&mut s2);
    println!("{}", s2); // "halo, dunia"

    // ANTI-PATTERN: lebih dari satu mutable borrow bersamaan
    let mut s3 = String::from("test");
    let r1 = &mut s3;
    // let r2 = &mut s3; // error: tidak bisa borrow s3 sebagai mutable lebih dari sekali
    println!("{}", r1);
}

Lifetime #

Lifetime adalah cara Rust memastikan referensi selalu valid selama digunakan. Dalam banyak kasus compiler bisa menginfer lifetime secara otomatis, tapi untuk fungsi yang mengembalikan referensi dari beberapa parameter, kamu perlu anotasi eksplisit.

// Tanpa anotasi lifetime, compiler tidak bisa tahu
// apakah return value mengacu ke x atau y
// Anotasi 'a berarti: return value punya lifetime minimal sepanjang x dan y
fn terpanjang<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() { x } else { y }
}

fn main() {
    let string1 = String::from("string panjang");
    let hasil;
    {
        let string2 = String::from("xyz");
        hasil = terpanjang(string1.as_str(), string2.as_str());
        println!("String terpanjang: {}", hasil); // ✓ valid di sini
    }
    // println!("{}", hasil); // error — string2 sudah tidak valid
}

Modul dan Visibilitas #

Rust menggunakan sistem modul untuk mengorganisir kode. Semua item (fungsi, struct, enum, dll.) private secara default — harus eksplisit ditandai pub untuk bisa diakses dari luar modul.

// Modul bisa didefinisikan inline atau di file terpisah
mod geometri {
    // Private secara default — hanya bisa diakses di dalam modul ini
    fn luas_internal(lebar: f64, tinggi: f64) -> f64 {
        lebar * tinggi
    }

    // pub — bisa diakses dari luar modul
    pub struct Persegi {
        pub lebar: f64,   // field pub — bisa diakses dari luar
        tinggi: f64,      // field private — tidak bisa diakses dari luar
    }

    impl Persegi {
        pub fn baru(lebar: f64, tinggi: f64) -> Self {
            Persegi { lebar, tinggi }
        }

        pub fn luas(&self) -> f64 {
            luas_internal(self.lebar, self.tinggi) // boleh akses fungsi private di modul yang sama
        }
    }

    pub mod lingkaran {
        pub fn luas(radius: f64) -> f64 {
            std::f64::consts::PI * radius * radius
        }
    }
}

fn main() {
    // use untuk mempersingkat path
    use geometri::Persegi;
    use geometri::lingkaran;

    let p = Persegi::baru(10.0, 5.0);
    println!("Luas persegi: {}", p.luas());
    println!("Lebar: {}", p.lebar); // ✓ field pub
    // println!("{}", p.tinggi);    // ✗ error: field private

    println!("Luas lingkaran r=7: {:.2}", lingkaran::luas(7.0));
}

Ringkasan #

  • Immutable by default — variabel di Rust tidak bisa diubah kecuali dideklarasikan dengan mut. Ini bukan batasan tapi desain yang mendorong kode lebih aman.
  • Shadowing ≠ mutasilet x = x + 1 membuat variabel baru, bukan mengubah variabel lama. Bahkan bisa mengubah tipe sekaligus.
  • match memaksa exhaustiveness — semua kemungkinan harus ditangani, compiler error jika ada yang terlewat. Gunakan _ sebagai wildcard untuk case lainnya.
  • Expression-orientedif, match, dan blok kode {} bisa mengembalikan nilai. Ekspresi tanpa titik koma di akhir fungsi adalah return value implisit.
  • Option<T> menggantikan null — tidak ada null di Rust. Nilai yang mungkin tidak ada direpresentasikan sebagai Some(nilai) atau None.
  • Result<T, E> untuk error — operasi yang bisa gagal mengembalikan Result. Operator ? mempersingkat propagasi error secara dramatis.
  • Ownership memiliki tiga aturan — setiap nilai punya tepat satu owner; owner bisa di-move, di-borrow (&T), atau di-borrow secara mutable (&mut T); nilai di-drop otomatis saat owner keluar scope.
  • Semua item private secara default — fungsi, struct, dan field harus ditandai pub secara eksplisit untuk bisa diakses dari luar modul.
  • Trait adalah kontrak perilaku — bisa diimplementasikan untuk tipe apapun, bahkan tipe dari library lain, selama kamu mendefinisikan trait-nya.

← Sebelumnya: Instalasi   Berikutnya: Komentar →

About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact