# Memahami Lifetime di Rust: Anotasi, Elision, dan Pertanyaan Interview 2026 > Panduan lengkap tentang lifetime Rust untuk persiapan interview. Pelajari anotasi lifetime, aturan elision, borrow checker, dan pertanyaan teknis umum dengan contoh kode praktis. - Published: 2026-08-27 - Updated: 2026-08-27 - Author: Anthony Fillion-Maillet - Reading time: 12 min --- Lifetime di Rust adalah mekanisme compiler untuk melacak berapa lama referensi tetap valid. Berbeda dengan bahasa yang menggunakan garbage collection di mana manajemen memori terjadi saat runtime, borrow checker Rust memvalidasi keabsahan referensi pada waktu kompilasi, menghilangkan seluruh kategori bug memori sebelum kode dijalankan. > **Jawaban Cepat untuk Interview** > > Lifetime di Rust adalah konstruk waktu kompilasi yang mendeskripsikan scope di mana sebuah referensi tetap valid. Borrow checker menggunakan lifetime untuk memastikan referensi tidak pernah bertahan lebih lama dari data yang dirujuknya, mencegah dangling pointer tanpa overhead runtime. ## Apa yang Sebenarnya Direpresentasikan Lifetime dalam Memori Lifetime bukan tentang berapa lama nilai ada. Lifetime mendeskripsikan berapa lama *referensi* ke nilai tetap valid. Setiap referensi di Rust memiliki lifetime, bahkan ketika anotasi dihilangkan. Perhatikan fungsi berikut yang ditolak compiler: ```rust // dangling_reference.rs fn create_dangling() -> &String { let s = String::from("hello"); &s // ERROR: `s` is dropped at end of function } ``` String `s` hanya hidup dalam scope fungsi. Mengembalikan referensi ke sana akan membuat dangling pointer, karena memori String di-dealokasi ketika fungsi mengembalikan nilai. Borrow checker menangkap ini pada waktu kompilasi. Perbaikannya memerlukan pengembalian nilai yang dimiliki atau memastikan data yang direferensikan bertahan lebih lama dari pemanggilan fungsi: ```rust // valid_return.rs // Option 1: Return owned value fn create_owned() -> String { String::from("hello") } // Option 2: Reference data that outlives the function fn first_word(s: &str) -> &str { s.split_whitespace().next().unwrap_or("") } ``` Dalam `first_word`, `&str` yang dikembalikan meminjam dari input `s`, sehingga tetap valid selama `s` valid. Pemanggil mengontrol lifetime input. ## Sintaks dan Semantik Anotasi Lifetime Anotasi lifetime eksplisit menggunakan sintaks `'a`, `'b`, dan seterusnya. Ini bukan instruksi ke compiler tentang berapa lama referensi harus hidup. Anotasi ini mendeskripsikan hubungan antara lifetime dari beberapa referensi. Dokumentasi [Rust Reference](https://doc.rust-lang.org/reference/trait-bounds.html#lifetime-bounds) mendefinisikan anotasi lifetime sebagai parameter generik yang membatasi berapa lama referensi harus tetap valid relatif satu sama lain. ```rust // lifetime_annotations.rs // Both inputs and output share the same lifetime 'a fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { if x.len() > y.len() { x } else { y } } fn main() { let string1 = String::from("long string"); let result; { let string2 = String::from("short"); result = longest(&string1, &string2); println!("Longest: {}", result); // Valid: both strings alive } // println!("{}", result); // ERROR: string2 dropped } ``` Anotasi `'a` memberitahu compiler: referensi yang dikembalikan akan valid untuk *irisan* dari lifetime `x` dan `y`. Karena `string2` memiliki lifetime lebih pendek, `result` tidak dapat digunakan setelah `string2` di-drop. Beberapa lifetime yang berbeda mengekspresikan hubungan yang lebih kompleks: ```rust // multiple_lifetimes.rs // Output tied only to first parameter's lifetime fn first_only<'a, 'b>(x: &'a str, _y: &'b str) -> &'a str { x } fn main() { let owned = String::from("owned"); let result; { let temporary = String::from("temporary"); result = first_only(&owned, &temporary); } // result still valid: only depends on owned println!("{}", result); } ``` ## Aturan Elision Lifetime di Rust 2024 Compiler menerapkan tiga aturan elision untuk menyimpulkan lifetime ketika anotasi dihilangkan. Aturan-aturan ini, didokumentasikan dalam [Rustonomicon](https://doc.rust-lang.org/nomicon/lifetime-elision.html), mengurangi boilerplate tanpa mengorbankan keamanan. > **Tiga Aturan Elision** > > 1. Setiap referensi input mendapat parameter lifetime-nya sendiri > 2. Jika tepat satu input lifetime ada, ia diterapkan ke semua referensi output > 3. Jika `&self` atau `&mut self` ada, lifetime-nya diterapkan ke semua referensi output Aturan-aturan ini menangani sebagian besar pola umum: ```rust // elision_examples.rs // Rule 1: Each input gets own lifetime fn takes_two(x: &str, y: &str) {} // Compiler reads: fn takes_two<'a, 'b>(x: &'a str, y: &'b str) {} // Rule 2: Single input lifetime propagates to output fn first_char(s: &str) -> &str { &s[0..1] } // Compiler reads: fn first_char<'a>(s: &'a str) -> &'a str // Rule 3: &self lifetime propagates to output impl Parser { fn peek(&self) -> &Token { &self.tokens[self.position] } // Compiler reads: fn peek<'a>(&'a self) -> &'a Token } ``` Ketika elision gagal, compiler memerlukan anotasi eksplisit. Ini paling sering terjadi dengan fungsi yang mengembalikan referensi yang diturunkan dari beberapa input: ```rust // elision_fails.rs // ERROR: Can't determine output lifetime fn ambiguous(x: &str, y: &str) -> &str { if x.len() > y.len() { x } else { y } } // FIX: Explicit annotation resolves ambiguity fn unambiguous<'a>(x: &'a str, y: &'a str) -> &'a str { if x.len() > y.len() { x } else { y } } ``` ## Lifetime Struct dan Hubungan Outlives Struct yang berisi referensi memerlukan anotasi lifetime untuk mengekspresikan bahwa data yang dipinjam harus bertahan lebih lama dari struct: ```rust // struct_lifetimes.rs struct Excerpt<'a> { text: &'a str, } impl<'a> Excerpt<'a> { // new borrows from input, so Excerpt can't outlive the source fn new(source: &'a str, start: usize, end: usize) -> Self { Excerpt { text: &source[start..end] } } // level() returns owned data, no lifetime in signature fn level(&self) -> u32 { self.text.len() as u32 / 10 } } fn main() { let novel = String::from("Call me Ishmael. Some years ago..."); let excerpt = Excerpt::new(&novel, 0, 16); println!("Excerpt: {}", excerpt.text); } // novel dropped, but excerpt already out of scope ``` `'a` dalam `Excerpt<'a>` mengikat validitas struct ke lifetime `text` yang dipinjam. Mencoba menggunakan `Excerpt` setelah String sumbernya di-drop memicu error kompilasi. Untuk struct dengan beberapa referensi, masing-masing dapat memiliki lifetime yang berbeda: ```rust // multiple_struct_lifetimes.rs struct Comparison<'a, 'b> { baseline: &'a str, candidate: &'b str, } impl<'a, 'b> Comparison<'a, 'b> { fn new(baseline: &'a str, candidate: &'b str) -> Self { Comparison { baseline, candidate } } fn baseline_only(&self) -> &'a str { self.baseline } } ``` ## Lifetime 'static dan Kapan Menggunakannya Lifetime `'static` menunjukkan data yang hidup selama eksekusi program. String literal memiliki lifetime ini karena tertanam dalam binary: ```rust // static_lifetime.rs let s: &'static str = "I live forever"; // Constants are implicitly 'static const CONFIG_VERSION: &str = "2.0.0"; // Thread-safe globals require 'static static COUNTER: AtomicU64 = AtomicU64::new(0); ``` Pola umum melibatkan bound `T: 'static`, yang sering membingungkan developer. Bound ini berarti `T` tidak berisi referensi non-static, bukan berarti `T` harus berupa referensi: ```rust // static_bound.rs use std::thread; fn spawn_task(data: T) { thread::spawn(move || { // data moved into thread, must live independently println!("Processing in thread"); }); } fn main() { let owned = String::from("owned data"); spawn_task(owned); // OK: String is 'static (owns its data) let reference = "borrowed"; // spawn_task(reference); // OK: &'static str } ``` Thread mungkin bertahan lebih lama dari fungsi yang membuatnya, sehingga data yang dipindahkan ke thread tidak boleh berisi referensi ke variabel stack-local. ## Error Lifetime Umum dan Solusinya Beberapa pola secara konsisten membuat developer tersandung. Memahami ini mempercepat debugging. **Mengembalikan referensi ke variabel lokal:** ```rust // error_local_ref.rs fn bad_split(text: &str, delimiter: char) -> (&str, &str) { let parts: Vec<&str> = text.split(delimiter).collect(); (parts[0], parts[1]) // OK: parts[i] borrow from text, not from Vec } fn truly_bad() -> &str { let local = String::from("local"); &local // ERROR: local dropped at end of function } ``` **Menyimpan referensi dalam struct dengan lifetime yang tidak cocok:** ```rust // error_struct_lifetime.rs struct Cache { data: String, // view: &str, // ERROR: needs lifetime parameter } // Self-referential structs require unsafe or crates like ouroboros struct CacheWithView<'a> { data: String, view: Option<&'a str>, // Can't point to self.data safely } ``` Struct self-referential, di mana sebuah field mereferensikan field lain, memerlukan `Pin` dengan kode unsafe atau crate seperti [ouroboros](https://docs.rs/ouroboros/). Modul [Rust smart pointers](/technologies/rust/interview-questions/smart-pointers) membahas pola-pola lanjutan ini. **Bound lifetime pada implementasi trait:** ```rust // trait_lifetime_bounds.rs trait Processor { fn process<'a>(&self, input: &'a str) -> &'a str; } struct Prefixer { prefix: String, } impl Processor for Prefixer { // Can't return &format!(...) - would be temporary fn process<'a>(&self, input: &'a str) -> &'a str { input // Must return input or part of it } } ``` ## Higher-Ranked Trait Bounds (HRTBs) untuk Lifetime Generik Higher-ranked trait bounds menggunakan sintaks `for<'a>` untuk mengekspresikan bahwa sebuah tipe harus memenuhi trait untuk *lifetime apa pun*, bukan hanya yang spesifik: ```rust // hrtb.rs use std::fmt::Debug; // F must be callable with any lifetime 'a fn apply_to_refs(f: F) where F: for<'a> Fn(&'a str) -> &'a str, { let owned = String::from("test"); let result = f(&owned); println!("{}", result); } fn identity(s: &str) -> &str { s } fn main() { apply_to_refs(identity); } ``` HRTBs sering muncul dalam API yang menerima closure dan ekosistem [Rust async/await](/blog/rust/rust-async-await-tokio-futures-concurrency) di mana futures harus bekerja dengan referensi dari berbagai lifetime. ## Pertanyaan Interview tentang Lifetime Rust Interview teknis menguji pemahaman tentang lifetime pada berbagai tingkat kedalaman. Pertanyaan-pertanyaan ini sering muncul dalam posisi Rust. **Pertanyaan: Mengapa kode ini gagal dikompilasi?** ```rust // interview_q1.rs fn get_str() -> &str { "hello" } ``` **Jawaban:** Tipe kembalian memerlukan anotasi lifetime eksplisit. Meskipun string literal memiliki lifetime `'static`, signature fungsi tidak mengekspresikan ini. Perbaikannya: `fn get_str() -> &'static str`. **Pertanyaan: Jelaskan mengapa ini dikompilasi dan apakah aman:** ```rust // interview_q2.rs fn longest<'a>(x: &'a str, _y: &str) -> &'a str { x } ``` **Jawaban:** Ini dikompilasi karena lifetime output hanya terikat pada `x`. Parameter `_y` dapat memiliki lifetime apa pun karena nilai kembalian tidak bergantung padanya. Ini aman: validitas referensi yang dikembalikan hanya bergantung pada lifetime `x`. **Pertanyaan: Apa yang terjadi ketika mencoba menyimpan referensi bersama data yang dimiliki?** ```rust // interview_q3.rs struct Config<'a> { name: String, description: &'a str, } ``` **Jawaban:** Pola ini valid tetapi membatasi bagaimana `Config` dapat digunakan. Struct tidak dapat bertahan lebih lama dari apa pun yang direferensikan oleh `description`. Untuk data yang dimiliki yang perlu mereferensikan dirinya sendiri, pertimbangkan menggunakan `String` untuk kedua field, atau teknik yang dibahas dalam [Rust ownership and borrowing](/blog/rust/rust-ownership-borrowing-demystified). **Pertanyaan: Bagaimana lifetime berinteraksi dengan trait objects?** ```rust // interview_q4.rs trait Formatter { fn format(&self, input: &str) -> String; } fn get_formatter<'a>() -> Box { // ... } ``` **Jawaban:** Trait objects memiliki bound lifetime `'static` implisit secara default. Menulis `Box` secara eksplisit memungkinkan trait object berisi referensi dengan lifetime `'a`. Tanpa bound eksplisit, `Box` sama dengan `Box`. ## Variance Lifetime: Covariance dan Contravariance Variance menentukan bagaimana lifetime berhubungan ketika tipe bersarang. Referensi Rust mengikuti aturan-aturan ini: - `&'a T` adalah **covariant** dalam `'a`: lifetime yang lebih panjang dapat menggantikan yang lebih pendek - `&'a mut T` adalah **invariant** dalam `T`: tipe harus cocok persis - `fn(&'a T)` adalah **contravariant** dalam `'a`: lifetime yang lebih pendek dapat menggantikan yang lebih panjang ```rust // variance.rs fn covariant_example() { let s: &'static str = "static"; let r: &str = s; // OK: 'static lives longer than any 'a } fn invariant_example() { let mut vec: Vec<&'static str> = vec!["a"]; // let s = String::from("local"); // vec.push(&s); // ERROR: &s is not &'static str } ``` Memahami variance penting ketika mendesain API generik yang menerima atau mengembalikan referensi. Artikel [Rust traits and generics](/blog/rust/rust-traits-generics-advanced-guide) membahas pola generik lanjutan. ## Anotasi Lifetime dalam Praktik: Poin-Poin Utama - Lifetime mendeskripsikan validitas referensi, bukan keberadaan nilai. Borrow checker menggunakannya untuk mencegah dangling pointer pada waktu kompilasi. - Aturan elision menangani sebagian besar kasus secara otomatis. Anotasi eksplisit diperlukan ketika mengembalikan referensi yang diturunkan dari beberapa input. - Lifetime struct mengekspresikan hubungan outlives: setiap struct yang berisi referensi harus diparameterisasi oleh lifetime referensi tersebut. - Bound `'static` pada generik berarti "tidak berisi referensi non-static," bukan "harus berupa referensi." - Struct self-referential memerlukan penanganan khusus melalui `Pin`, kode unsafe, atau crate helper. - Pertanyaan interview fokus pada pemahaman mengapa kode gagal dikompilasi dan bagaimana anotasi lifetime mengubah jaminan validitas. --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/id/blog/rust/rust-lifetimes-explained-annotations-elision-interview