# Memahami Ownership dan Borrowing di Rust: Panduan Lengkap untuk Developer > Pelajari konsep ownership, borrowing, dan lifetime di Rust secara mendalam. Artikel ini membahas aturan kepemilikan, referensi, serta pola-pola umum yang sering muncul dalam wawancara teknis. - Published: 2026-03-31 - Updated: 2026-03-31 - Author: Anthony Fillion-Maillet - Tags: rust, ownership, borrowing, memory-management, interview - Reading time: 8 min --- Rust dikenal sebagai bahasa pemrograman yang menjamin keamanan memori tanpa memerlukan garbage collector. Rahasianya terletak pada sistem **ownership** dan **borrowing** yang diterapkan pada saat kompilasi. Bagi para developer yang sedang mempersiapkan wawancara teknis atau ingin menguasai Rust secara mendalam, pemahaman terhadap kedua konsep ini merupakan fondasi yang tidak bisa diabaikan. Artikel ini membahas secara menyeluruh bagaimana ownership bekerja, aturan borrowing yang harus dipatuhi, serta pola-pola praktis yang sering ditemui dalam kode Rust profesional. > **Mengapa Topik Ini Penting untuk Wawancara?** > > Hampir setiap wawancara teknis Rust akan menguji pemahaman kandidat terhadap ownership dan borrowing. Konsep ini menjadi pembeda utama Rust dari bahasa lain dan merupakan indikator kuat kemampuan seorang developer dalam menulis kode yang aman dan efisien. ## Apa Itu Ownership di Rust? Ownership adalah mekanisme inti Rust dalam mengelola memori. Setiap nilai dalam Rust memiliki tepat satu **pemilik** (owner), dan ketika pemilik tersebut keluar dari scope, nilai tersebut secara otomatis di-dealokasi. Tidak ada garbage collector, tidak ada manual `free()` -- semuanya ditangani oleh compiler. Tiga aturan dasar ownership di Rust: 1. Setiap nilai memiliki satu variabel yang menjadi pemiliknya. 2. Hanya boleh ada satu pemilik pada satu waktu. 3. Ketika pemilik keluar dari scope, nilai tersebut akan di-drop. Konsep ini terlihat sederhana, tetapi implikasinya sangat luas terhadap cara penulisan kode sehari-hari. ## Move Semantics: Perpindahan Kepemilikan Ketika sebuah variabel ditetapkan ke variabel lain, kepemilikan berpindah. Proses ini disebut **move**. Setelah move terjadi, variabel asal tidak lagi valid. ```rust // move_semantics.rs fn main() { let original = String::from("interview prep"); let moved = original; // ownership transfers here // println!("{}", original); // compile error: value moved println!("{}", moved); // works fine } ``` Perhatikan bahwa compiler Rust menolak akses ke `original` setelah kepemilikan berpindah ke `moved`. Ini bukan error runtime, melainkan perlindungan yang diterapkan pada saat kompilasi. ## Copy vs Move: Kapan Nilai Disalin? Tidak semua tipe data mengalami move. Tipe-tipe primitif seperti `i32`, `f64`, dan `bool` mengimplementasikan trait `Copy`, yang berarti nilai tersebut akan disalin secara otomatis alih-alih dipindahkan. Untuk tipe yang dialokasikan di heap seperti `String`, developer perlu menggunakan method `.clone()` untuk membuat salinan eksplisit. ```rust // copy_vs_move.rs fn main() { let x: i32 = 42; let y = x; // copy, not move -- i32 is Copy println!("x = {}, y = {}", x, y); // both valid let s1 = String::from("hello"); let s2 = s1.clone(); // explicit deep copy println!("s1 = {}, s2 = {}", s1, s2); // both valid after clone } ``` Memahami perbedaan antara Copy dan Move sangat penting. Dalam wawancara teknis, pertanyaan tentang kapan sebuah nilai di-copy versus di-move merupakan salah satu yang paling sering diajukan. > **Tipe-Tipe yang Mengimplementasikan Copy** > > Semua tipe skalar (integer, floating-point, boolean, char) serta tuple yang hanya berisi tipe Copy secara otomatis mengimplementasikan trait Copy. Tipe yang memerlukan alokasi heap seperti String, Vec, dan HashMap tidak mengimplementasikan Copy. ## Immutable Borrowing: Meminjam Tanpa Mengubah Borrowing memungkinkan sebuah fungsi mengakses data tanpa mengambil alih kepemilikannya. Referensi immutable (`&T`) memberikan akses baca-saja terhadap sebuah nilai. ```rust // immutable_borrowing.rs fn calculate_length(s: &String) -> usize { s.len() // read access through the reference } // s goes out of scope, but doesn't drop the String (not the owner) fn main() { let greeting = String::from("hello, Rust"); let len = calculate_length(&greeting); // borrow, don't move println!("'{}' has {} characters", greeting, len); // greeting still valid } ``` Dengan borrowing, fungsi `calculate_length` dapat membaca isi `greeting` tanpa memindahkan kepemilikan. Setelah fungsi selesai, variabel `greeting` tetap valid dan dapat digunakan kembali. Rust mengizinkan banyak referensi immutable secara bersamaan. Hal ini aman karena tidak ada pihak yang mengubah data. ## Mutable Borrowing: Meminjam dan Mengubah Ketika sebuah fungsi perlu memodifikasi data yang dipinjam, digunakan referensi mutable (`&mut T`). Rust menerapkan aturan ketat: hanya boleh ada **satu** referensi mutable pada satu waktu. ```rust // mutable_borrowing.rs fn append_greeting(s: &mut String) { s.push_str(", welcome to Rust!"); // modify through mutable ref } fn main() { let mut message = String::from("Hello"); append_greeting(&mut message); println!("{}", message); // "Hello, welcome to Rust!" } ``` Aturan ini mencegah terjadinya **data race** pada saat kompilasi. Data race terjadi ketika dua pointer mengakses data yang sama secara bersamaan dan salah satunya melakukan penulisan. Dengan membatasi mutable borrow menjadi satu saja, Rust mengeliminasi kategori bug ini sepenuhnya. ## Aturan Eksklusivitas: Satu Mutable atau Banyak Immutable Rust menerapkan aturan eksklusivitas yang tegas: pada satu waktu, sebuah nilai hanya boleh memiliki **satu referensi mutable** ATAU **banyak referensi immutable**, tetapi tidak keduanya sekaligus. ```rust // exclusivity_rule.rs fn main() { let mut data = String::from("shared state"); let r1 = &mut data; // let r2 = &mut data; // compile error: second mutable borrow println!("{}", r1); // After r1's last usage, a new mutable borrow is allowed let r3 = &mut data; // this works -- non-lexical lifetimes r3.push_str(" updated"); println!("{}", r3); } ``` Perlu diperhatikan konsep **Non-Lexical Lifetimes (NLL)** yang diperkenalkan di Rust edisi 2018. Dengan NLL, compiler cukup cerdas untuk mengetahui kapan sebuah referensi terakhir kali digunakan, sehingga borrow berakhir lebih awal daripada akhir scope. Inilah sebabnya `r3` dapat dibuat setelah penggunaan terakhir `r1`. ## Lifetime Annotations: Menjelaskan Masa Hidup Referensi Lifetime adalah cara Rust memastikan bahwa referensi tidak pernah menunjuk ke data yang sudah di-dealokasi (dangling reference). Dalam kebanyakan kasus, compiler dapat menyimpulkan lifetime secara otomatis melalui **lifetime elision rules**. Namun, dalam situasi tertentu, developer harus menuliskan anotasi lifetime secara eksplisit. ```rust // lifetime_annotation.rs // 'a declares: the returned reference lives as long as // the shortest-lived input reference fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { if x.len() > y.len() { x } else { y } } fn main() { let result; let string1 = String::from("Rust ownership"); { let string2 = String::from("borrowing"); result = longest(string1.as_str(), string2.as_str()); println!("Longest: {}", result); // valid: both strings alive } // println!("{}", result); // would fail: string2 dropped } ``` Anotasi `'a` pada fungsi `longest` menyatakan bahwa referensi yang dikembalikan akan hidup selama referensi input yang paling pendek masa hidupnya. Compiler menggunakan informasi ini untuk mencegah dangling reference. ## Lifetime pada Struct Ketika sebuah struct menyimpan referensi, struct tersebut harus mendeklarasikan parameter lifetime. Ini memberitahu compiler bahwa struct tidak boleh hidup lebih lama dari data yang dirujuknya. ```rust // struct_lifetime.rs struct Excerpt<'a> { text: &'a str, // this struct borrows a string slice } impl<'a> Excerpt<'a> { fn summary(&self) -> &str { let end = self.text.len().min(20); &self.text[..end] // return a slice of the borrowed text } } fn main() { let article = String::from("Rust ownership model eliminates memory bugs"); let excerpt = Excerpt { text: article.as_str(), }; println!("Summary: {}", excerpt.summary()); } ``` Pola ini sangat umum dalam kode Rust. Struct `Excerpt` meminjam string slice dari `article`, dan compiler memastikan `excerpt` tidak pernah hidup lebih lama dari `article`. > **Kesalahan Umum dengan Lifetime** > > Salah satu kesalahan yang sering dilakukan developer pemula adalah mencoba mengembalikan referensi ke data yang dibuat di dalam fungsi. Karena data tersebut akan di-drop ketika fungsi berakhir, referensi menjadi invalid. Solusinya adalah mengembalikan nilai yang dimiliki (owned value) alih-alih referensi. ## Pola-Pola Ownership yang Umum Dalam praktik sehari-hari, terdapat tiga pola utama yang digunakan developer Rust untuk mengelola ownership dan borrowing secara efektif. ```rust // ownership_patterns.rs // Pattern 1: Take ownership, return a new value fn process_and_return(mut input: String) -> String { input.push_str(" -- processed"); input // ownership transfers to caller } // Pattern 2: Borrow for read-only inspection fn contains_keyword(text: &str, keyword: &str) -> bool { text.to_lowercase().contains(&keyword.to_lowercase()) } // Pattern 3: Borrow mutably for in-place modification fn sanitize(input: &mut String) { *input = input.trim().to_string(); } fn main() { // Pattern 1 let raw = String::from("user input"); let processed = process_and_return(raw); // raw is now invalid, processed owns the data // Pattern 2 let found = contains_keyword(&processed, "input"); println!("Contains 'input': {}", found); // Pattern 3 let mut padded = String::from(" spaces everywhere "); sanitize(&mut padded); println!("Sanitized: '{}'", padded); } ``` **Pola 1** cocok ketika fungsi perlu mengambil alih data, melakukan transformasi, dan mengembalikan hasilnya. **Pola 2** ideal untuk operasi baca-saja yang tidak perlu memiliki data. **Pola 3** digunakan ketika modifikasi in-place diperlukan tanpa memindahkan kepemilikan. Memilih pola yang tepat merupakan keterampilan penting. Sebagai pedoman umum, gunakan borrowing sebisa mungkin dan hanya ambil ownership ketika benar-benar diperlukan. ## Mengatasi Error Kompilasi yang Sering Muncul Bagi developer yang baru belajar Rust, error kompilasi terkait ownership bisa terasa membingungkan. Berikut adalah tiga error paling umum beserta cara mengatasinya. ```rust // common_fixes.rs fn main() { // Error E0502: cannot borrow as mutable because also borrowed as immutable let mut scores = vec![90, 85, 78]; // Fix: finish using the immutable borrow before mutating let first = scores[0]; // copy (i32 is Copy), no active borrow scores.push(95); // mutable borrow -- no conflict println!("First: {}, All: {:?}", first, scores); // Error E0382: use of moved value let name = String::from("Alice"); let greeting = format!("Hello, {}", name); // format! borrows, doesn't move println!("{} says {}", name, greeting); // both valid // Error E0597: borrowed value does not live long enough let outer; { let inner = String::from("temporary"); outer = inner; // move instead of borrow -- extends lifetime } println!("{}", outer); // works: outer owns the value } ``` **E0502** terjadi ketika immutable dan mutable borrow aktif secara bersamaan. Solusinya adalah menyelesaikan penggunaan immutable borrow sebelum membuat mutable borrow. **E0382** muncul ketika menggunakan variabel yang sudah di-move. Solusinya bisa berupa penggunaan `.clone()`, borrowing, atau memanfaatkan macro seperti `format!` yang hanya meminjam. **E0597** terjadi ketika referensi menunjuk ke data yang sudah keluar dari scope. Solusinya adalah memindahkan kepemilikan alih-alih meminjam, sehingga masa hidup data diperpanjang. ## Kesimpulan Ownership dan borrowing merupakan pilar utama keamanan memori di Rust. Sistem ini memungkinkan Rust memberikan jaminan keamanan memori tanpa overhead runtime dari garbage collector. Tiga aturan dasar ownership, dikombinasikan dengan aturan eksklusivitas borrowing dan lifetime annotations, membentuk kerangka kerja yang konsisten untuk pengelolaan memori. Bagi developer yang mempersiapkan wawancara teknis, penguasaan konsep-konsep ini bukan sekadar keharusan teori, melainkan juga kemampuan praktis yang akan diuji melalui pertanyaan kode dan sesi live coding. Membiasakan diri membaca dan memahami error compiler Rust adalah langkah terbaik untuk memperdalam pemahaman terhadap ownership dan borrowing. Dengan latihan yang konsisten, pola-pola ownership akan menjadi intuitif, dan pesan error compiler Rust akan berubah dari hambatan menjadi panduan menuju kode yang lebih aman dan efisien. --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/id/blog/rust/rust-ownership-borrowing-demystified