Lewati ke konten

Error Rust E0597: borrowed value does not live long enough (nilai sudah di-drop)

E0597 artinya: kamu menyimpan sebuah referensi (&x) lebih lama daripada nilai x itu sendiri. Nilainya dibersihkan di akhir blok tempat ia dibuat, tapi ada sesuatu di luar blok itu yang masih ingin memakai referensinya. Error ini sangat umum, terutama dengan blok di dalam blok, perulangan, dan struct, dan perbaikannya biasanya cukup memindahkan satu baris atau menyimpan nilai yang dimiliki sendiri (owned) alih-alih referensi. Jangan khawatir, ini membingungkan semua orang di awal. (Halaman ini bagian dari Error compiler Rust dijelaskan.)

error[E0597]: `x` does not live long enough
--> src/main.rs:5:13
|
4 | let x = 5;
| - binding `x` declared here
5 | r = &x; // r meminjam x
| ^^ borrowed value does not live long enough
6 | } // x di-drop di sini
| - `x` dropped here while still borrowed
7 | println!("r: {r}"); // ❌ r akan menunjuk ke sesuatu yang sudah tidak ada
| - borrow later used here

Ini program yang menghasilkannya:

fn main() {
let r;
{
let x = 5;
r = &x; // r meminjam x
} // x di-drop di sini
println!("r: {r}"); // ❌ r akan menunjuk ke sesuatu yang sudah tidak ada
}

Keempat label itu bercerita secara berurutan:

  1. Baris 4: x dibuat di dalam blok { ... } bagian dalam (binding x declared here).
  2. Baris 5: r meminjam x. Sejauh ini masih baik-baik saja.
  3. Baris 6: bloknya berakhir, jadi x di-drop (dibersihkan). Padahal borrow-nya masih hidup (dropped here while still borrowed).
  4. Baris 7: r dipakai, tapi nilai yang ditunjuknya sudah hilang (borrow later used here).

Bayangkan kamu mencatat nomor kamar hotel temanmu. Catatan itu berguna selama dia menginap di sana. Begitu dia check-out, nomornya masih ada di catatanmu, tapi sekarang nomor itu menuju kamar kosong, atau kamar orang asing. Rust tidak akan membiarkanmu menyimpan catatan yang menuju kamar kosong.

Kamu juga akan melihat label pendek “borrowed value does not live long enough” (nilai yang dipinjam tidak hidup cukup lama). Itu error yang sama; judulnya hanya menyebut nama variabelnya.

Sebuah referensi tidak boleh hidup lebih lama dari nilai yang ditunjuknya. Kalau itu terjadi, kamu punya dangling reference (referensi menggantung): pointer ke memori yang sudah dibebaskan dan mungkin sekarang berisi hal lain. Di C dan C++, ini sumber klasik crash dan celah keamanan (“use after free”).

Setiap nilai di Rust di-drop saat pemiliknya keluar dari scope, biasanya di } penutup blok tempat nilai itu dideklarasikan. Borrow checker membandingkan dua rentang: berapa lama nilainya hidup dan berapa lama referensinya dipakai. Kalau referensinya dipakai setelah } milik nilai itu, kamu mendapat E0597. Lihat Scope, Borrowing, dan Lifetime.

Perhatikan apa yang tidak dikeluhkan compiler: meminjam x di baris 5. Masalahnya hanya pemakaian belakangan di baris 7. Hapus baris terakhir itu, dan programnya bisa dikompilasi. Ini petunjuk berguna untuk perbaikan di bawah: buat nilainya hidup lebih lama, atau buat pemakaian referensinya berakhir lebih cepat.

Perbaikan 1: Pindahkan nilainya ke scope yang lebih luar

Section titled “Perbaikan 1: Pindahkan nilainya ke scope yang lebih luar”

Pilih ini kalau nilainya memang dibutuhkan selama referensinya dipakai. Ini perbaikan yang paling umum: deklarasikan nilainya sebelum (di luar) blok, supaya ia hidup setidaknya selama referensinya.

fn main() {
let scores = vec![72, 95, 88]; // sekarang hidup sampai akhir main
let best;
{
best = scores.iter().max().unwrap();
}
println!("Skor terbaik: {best}"); // ✅ scores masih hidup
}

Output:

Skor terbaik: 95
▶ Coba sendiri

Aturan praktisnya: pemilik harus dideklarasikan sebelum peminjam dan hidup lebih lama darinya. Kalau kamu melihat nilai yang dibuat di dalam blok atau perulangan, dan referensinya dipakai di luar, pindahkan nilainya keluar.

Perbaikan 2: Simpan nilai milik sendiri, bukan referensi

Section titled “Perbaikan 2: Simpan nilai milik sendiri, bukan referensi”

Pilih ini kalau kamu mengumpulkan nilai yang dibuat di dalam perulangan. Setiap putaran membuat String baru dan men-drop-nya di akhir putaran, jadi Vec<&str> di luar perulangan tidak bisa menyimpan referensi ke string-string itu:

fn main() {
let mut names: Vec<&str> = Vec::new();
for i in 1..=3 {
let name = format!("player{i}");
names.push(&name); // ❌ `name` does not live long enough
}
println!("{names:?}");
}

Perbaikannya adalah membiarkan Vec memiliki string-nya. Ubah Vec<&str> menjadi Vec<String> dan push String-nya langsung (ini memindahkannya ke dalam vektor, jadi ia hidup selama vektornya hidup):

fn main() {
let mut names: Vec<String> = Vec::new();
for i in 1..=3 {
let name = format!("player{i}");
names.push(name); // pindahkan String ke dalam Vec
}
println!("{names:?}");
}

Output:

["player1", "player2", "player3"]
▶ Coba sendiri

Ide yang sama berlaku untuk struct dan HashMap: kalau datanya dibuat di scope yang lebih kecil daripada wadahnya, simpan String (atau Vec<T>) alih-alih &str (atau &[T]). Field yang dimiliki sendiri hampir selalu pilihan yang tepat untuk pemula. Lihat String dan Vektor.

Pilih ini kalau kamu juga masih perlu memakai nilai aslinya, atau kamu hanya punya borrow-nya. Struct yang menyimpan referensi (Label<'a> { text: &'a str }) tidak bisa hidup lebih lama dari string yang ditunjuknya. Beri struct itu field String milik sendiri dan simpan salinannya:

struct Label {
text: String, // dimiliki sendiri, bukan &str
}
fn main() {
let label;
{
let title = String::from("Inbox");
label = Label { text: title.clone() };
println!("Di dalam blok: {title}");
}
println!("Label: {}", label.text); // ✅ label punya salinannya sendiri
}

Output:

Di dalam blok: Inbox
Label: Inbox
▶ Coba sendiri

Kalau kamu tidak butuh title lagi sesudahnya, lewati clone-nya dan pindahkan saja: Label { text: title }. Meng-clone string pendek itu murah dan sama sekali tidak masalah. Kalau kamu hanya punya &str, pakai .to_string() untuk mendapatkan salinan milik sendiri.

Perbaikan 4: Susun ulang supaya borrow-nya berakhir tepat waktu

Section titled “Perbaikan 4: Susun ulang supaya borrow-nya berakhir tepat waktu”

Pilih ini kalau nilainya memang dimaksudkan berumur pendek. Alih-alih membuat nilainya hidup lebih lama, pakai referensinya selagi nilainya masih hidup. Ini sering muncul pada fungsi yang mengembalikan referensi, seperti contoh klasik longest:

fn longest<'a>(a: &'a str, b: &'a str) -> &'a str {
if a.len() >= b.len() { a } else { b }
}
fn main() {
let first = String::from("Ferris the crab");
{
let second = String::from("Corro");
let winner = longest(&first, &second);
println!("Nama terpanjang: {winner}"); // ✅ dipakai sebelum second di-drop
}
}

Output:

Nama terpanjang: Ferris the crab
▶ Coba sendiri

Nama-namanya sengaja dibiarkan dalam bahasa Inggris, karena fungsi ini membandingkan panjang string. Kalau winner dideklarasikan di luar blok dan dicetak sesudahnya, kamu mendapat E0597 untuk second, walaupun kebetulan first lebih panjang. Signature-nya bilang hasilnya bisa meminjam dari argumen mana saja, jadi keduanya harus hidup lebih lama dari hasilnya. Kalau kamu benar-benar butuh hasilnya belakangan, simpan sebagai nilai milik sendiri: let winner = longest(&first, &second).to_string();.

Closure yang disimpan di luar blok. Closure yang memakai name akan meminjamnya. Kalau closure-nya hidup lebih lama dari name, kamu mendapat E0597 dengan label tambahan, value captured here (nilai ditangkap di sini):

let say_hello;
{
let name = String::from("Ferris");
say_hello = || println!("Hello, {name}!"); // ❌ `name` does not live long enough
}
say_hello();

Perbaikan: tulis move || ... supaya closure-nya mengambil ownership dari name. Lihat Closure.

Thread. Meminjam variabel lokal di dalam thread::spawn(|| ...) memberi error yang berbeda, E0373 (“closure may outlive the current function”), karena thread-nya bisa berjalan lebih lama dari main. Perbaikan: thread::spawn(move || ...), bagikan dengan Arc, atau pakai std::thread::scope. Lihat Konkurensi.

Referensi ke nilai sementara (temporary). let label = Label { text: String::from("Inbox").as_str() }; mirip E0597, tapi muncul sebagai E0716, “temporary value dropped while borrowed”: String-nya tidak pernah disimpan di variabel, jadi ia di-drop di akhir statement. Perbaikan: simpan dulu di variabel (let title = String::from("Inbox");), atau jadikan field-nya String.

Mengembalikan referensi dari fungsi. Kalau nilainya dibuat di dalam fungsi, kamu akan mendapat E0515. Kembalikan nilai milik sendiri.

Kuis singkat

1. Pada let r; { let x = 5; r = &x; } yang diikuti println! untuk r, apa sebenarnya yang memicu E0597?

2. Kamu membuat String di setiap putaran perulangan dan push &name ke Vec<&str>. Apa perbaikan yang biasa?

3. Closure di sebuah thread meminjam Vec lokal. Error apa yang kamu dapat?

Skor: 0 / 3