Sepanjang Course ini kita membangun kepercayaan bahwa kompilator Rust menjaga memori kita: ownership memastikan setiap nilai punya tepat satu pemilik, borrowing memastikan aliasing terkendali, dan trait Send/Sync yang kita pelajari di Section Koncurrency memastikan batas thread aman. Namun ada wilayah tempat kompilator benar-benar tidak sanggup memverifikasi kebenaran kode: ketika kita perlu berbicara dengan pustaka C yang sudah dikompilasi, membaca alamat perangkat keras, atau membangun struktur data fundamental seperti Vec yang secara teknis mustahil ditulis murni dengan operasi aman. Untuk wilayah itulah Rust menyediakan unsafe. Chapter ini memetakan apa yang sebenarnya dibuka oleh unsafe — lima kemampuan spesifik, bukan tuas “matikan kompilator” — serta tanggung jawab besar apa yang berpindah ke pundak kita begitu kata kunci itu ditulis.

Lima Kemampuan yang Dibuka unsafe #

Mitos paling berbahaya tentang unsafe adalah anggapan bahwa keyword ini mematikan seluruh pemeriksaan kompilator. Kenyataannya jauh lebih sempit: unsafe hanya membuka lima operasi spesifik yang semuanya berbagi satu ciri — kompilator tidak punya cukup informasi untuk membuktikan operasi tersebut aman. Tabel berikut merangkum kelimanya sekaligus memetakan lokasi pembahasannya di section ini:

KemampuanArtinyaDibahas di
Dereferensi raw pointerMembaca atau menulis nilai lewat *const T / *mut T tanpa jaminan borrow checkerChapter Raw Pointer dan Dereferensi Raw Pointer
Memanggil fungsi atau metode unsafeMenjalankan fungsi yang kontrak keamanannya tidak bisa diverifikasi kompilatorChapter Blok dan Fungsi unsafe
Mengakses dan mengubah static mutMembaca atau menulis variabel global yang dapat berubah, rawan race dataChapter Static Mutable
Mengimplementasikan unsafe traitMemberikan jaminan manual atas trait seperti SendChapter Static Mutable
Memanggil fungsi externMenjalankan fungsi pustaka luar (misalnya C) melalui ABIChapter FFI dengan extern

Perhatikan polanya: tidak ada satu pun baris yang berbunyi “melewati aturan ownership” atau “mengabaikan lifetime”. Semua aturan safe Rust tetap berlaku penuh di dalam kode unsafe; yang berubah hanyalah munculnya lima operasi tambahan.

unsafe Tidak Mematikan Borrow Checker #

Klaim “unsafe mematikan borrow checker” mudah dibantah dengan percobaan sederhana: pinjam dua reference mutable terhadap variabel yang sama di dalam blok unsafe. Program berikut tidak akan pernah dikompilasi:

rust
fn main() {
    let mut pesan = String::from("kopi tubruk");

    unsafe {
        let pinjam1 = &mut pesan;
        let pinjam2 = &mut pesan;
        println!("{} {}", pinjam1.len(), pinjam2.len());
    }
}

Kompilasi berhenti dengan pesan asli berikut:

text
warning: unnecessary `unsafe` block
 --> main.rs:4:5
  |
4 |     unsafe {
  |     ^^^^^^ unnecessary `unsafe` block
  |
  = note: `#[warn(unused_unsafe)]` (part of `#[warn(unused)]`) on by default

error[E0499]: cannot borrow `pesan` as mutable more than once at a time
 --> main.rs:6:23
  |
5 |         let pinjam1 = &mut pesan;
  |                       ---------- first mutable borrow occurs here
6 |         let pinjam2 = &mut pesan;
  |                       ^^^^^^^^^^ second mutable borrow occurs here
7 |         println!("{} {}", pinjam1.len(), pinjam2.len());
  |                           ------- first borrow later used here

error: aborting due to 1 previous error; 1 warning emitted

For more information about this error, try `rustc --explain E0499`.

Dua detail pada pesan tersebut layak digarisbawahi. Pertama, error E0499 tetap keluar meskipun pelanggaran terjadi di dalam blok unsafe — pemeriksaan borrowing, ownership, dan lifetime sama sekali tidak dinonaktifkan. Kedua, kompilator justru melaporkan warning unnecessary unsafe block karena isi blok tersebut tidak memuat satu pun dari lima operasi unsafe; blok itu “percuma” sejak awal. Pesan ganda ini menegaskan fungsi sesungguhnya dari unsafe: memberi izin yang sangat terbatas, bukan membatalkan hukum.

Garis Batas dalam Program Nyata #

Bentuk paling ringkas untuk melihat garis batas aman/berbahaya adalah mengubah nilai lewat raw pointer. Membuat raw pointer dari reference adalah operasi biasa; membaca atau menulis nilai yang ditunjuknya barulah membutuhkan unsafe:

rust
fn main() {
    let mut stok: i32 = 120;

    // Membuat raw pointer adalah operasi biasa, tidak butuh unsafe:
    let ptr_baca = &stok as *const i32;
    let ptr_tulis = &mut stok as *mut i32;

    println!("Stok awal: {}", stok);

    unsafe {
        // Dereferensi raw pointer hanya sah di dalam blok unsafe:
        *ptr_tulis += 30;
        println!("Setelah restock: {}", *ptr_baca);
    }

    println!("Stok akhir: {}", stok);
}

Output yang Dihasilkan:

text
Stok awal: 120
Setelah restock: 150
Stok akhir: 150

Mari kita bedah kode di atas baris per baris:

  1. Baris 2 (let mut stok: i32 = 120;): variabel stok dideklarasikan mutable karena program akan menulis nilai barunya lewat ptr_tulis.
  2. Baris 5 (let ptr_baca = &stok as *const i32;): reference &stok dikonversi menjadi raw pointer *const i32. Konversi seperti ini aman — kompilator tahu stok valid di titik ini — sehingga tidak perlu unsafe.
  3. Baris 6 (let ptr_tulis = &mut stok as *mut i32;): raw pointer *mut i32 dibuat dari reference mutable. Dua pointer kini menunjuk data yang sama, pola yang disebut aliasing dan mustahil dilakukan dua reference biasa.
  4. Baris 8 (println!("Stok awal: {}", stok);): nilai asli dicetak melalui variabelnya — bukti tidak ada kepemilikan yang berpindah ke pointer.
  5. Baris 10 (unsafe {): blok unsafe dibuka tepat di titik operasi berisiko dimulai. Praktik terbaik adalah membuat blok ini sekecil mungkin agar mudah diperiksa manusia.
  6. Baris 12 (*ptr_tulis += 30;): operator * melakukan dereferensi — membaca 120 dari memori lalu menulis 150. Di luar blok unsafe, baris ini ditolak dengan error E0133.
  7. Baris 13 (println!("Setelah restock: {}", *ptr_baca);): pembacaan lewat ptr_baca juga termasuk operasi unsafe sehingga harus berada di blok yang sama.
  8. Baris 16 (println!("Stok akhir: {}", stok);): setelah blok ditutup, semua kembali ke mode aman penuh — variabel biasa dicetak dengan jaminan kompilator.

Program ini sengaja dibuat benar agar terbaca jelas. Bayangkan ptr_tulis diambil dari variabel yang sudah di-drop, atau pointer bernilai null — kompilator tidak akan mencegah dereferensi tersebut di dalam blok unsafe, dan akibatnya berupa undefined behavior: crash, data rusak, atau tampak “berjalan normal” padahal salah.

Tanggung Jawab yang Berpindah ke Kita #

Konsep kuncinya bernama invariant: kondisi yang harus selalu terpenuhi agar operasi memori tetap sehat. Reference &mut T membawa invariant “pointer valid, terinisialisasi, tidak di-alias” — dan kompilator membuktikan invariant itu setiap kali kode dikompilasi. Raw pointer tidak membawa invariant apa pun; begitu kita dereferensi di dalam unsafe, pembuktian berpindah dari mesin ke manusia. Jika pembuktian kita keliru, program tidak sekadar error — program masuk wilayah undefined behavior yang perilakunya tidak dijanjikan siapa pun, termasuk kompilator itu sendiri.

Istilah penting lainnya adalah soundness: sebuah API disebut sound apabila pemanggil yang sepenuhnya patuh pada aturan safe Rust tidak mungkin memicu undefined behavior, sekalipun implementasi internalnya memakai unsafe. Seluruh standar library dibangun di atas prinsip ini — Vec, Mutex, hingga String semuanya berisi kode unsafe yang dibungkus API aman. Keputusan kapan unsafe wajar dipakai dapat kita petakan sebagai berikut:

Ringkasan #

unsafe membuka tepat lima kemampuan — dereferensi raw pointer, memanggil fungsi unsafe, mengakses static mut, mengimplementasikan unsafe trait, dan memanggil fungsi extern — tanpa mematikan satu pun aturan ownership, borrowing, atau lifetime. Error E0499 tetap muncul untuk dua pinjaman mutable di dalam blok unsafe, sementara warning unnecessary unsafe block menandai blok yang tidak berisi operasi unsafe sama sekali. Setiap operasi yang dibuka menyerahkan pembuktian invariant dari kompilator kepada programmer, dengan undefined behavior sebagai konsekuensi kegagalan pembuktian. Langkah berikutnya adalah mempelajari dua kendaraan sintaks tempat kelima kemampuan itu dieksekusi: blok unsafe dan fungsi unsafe.