Error Rust E0716: temporary value dropped while borrowed (nilai sementara)
E0716 artinya: kamu meminjam dari sebuah nilai yang tidak punya nama variabel, lalu terus memakai borrow itu di baris berikutnya. Rust membuang nilai tanpa nama seperti itu di akhir statement, jadi borrow-nya akan menunjuk ke sesuatu yang sudah tidak ada. Kelihatannya menakutkan, tapi ini salah satu error borrow yang paling mudah diperbaiki: biasanya kamu cukup memecah satu baris menjadi dua. (Halaman ini bagian dari Error compiler Rust dijelaskan.)
Pesan error-nya
Section titled “Pesan error-nya”error[E0716]: temporary value dropped while borrowed --> src/main.rs:2:13 |2 | let s = String::from("hi").as_str(); // s meminjam String yang tidak punya nama | ^^^^^^^^^^^^^^^^^^ - temporary value is freed at the end of this statement | | | creates a temporary value which is freed while still in use3 | println!("{s}"); // ❌ String itu sudah hilang | - borrow later used here |help: consider using a `let` binding to create a longer lived value |2 ~ let binding = String::from("hi");3 ~ let s = binding.as_str(); // s meminjam String yang tidak punya nama |Ini program yang menghasilkannya:
fn main() { let s = String::from("hi").as_str(); // s meminjam String yang tidak punya nama println!("{s}"); // ❌ String itu sudah hilang}Apa artinya
Section titled “Apa artinya”Temporary (nilai sementara) adalah nilai yang dibuat di tengah sebuah ekspresi tapi tidak pernah disimpan di variabel. Di baris di atas, String::from("hi") membuat String sungguhan, tapi tidak ada yang memilikinya: tidak ada let nama = ... untuknya. Rust men-drop (membersihkan) temporary di akhir statement, yaitu di ;.
Baca ketiga labelnya dari kiri ke kanan:
^^^^di bawahString::from("hi"): di sini sebuahStringsementara dibuat (creates a temporary value).-di akhir baris: di sini, di;,Stringitu dibebaskan (freed at the end of this statement).-di bawahsdi baris 3: tapis(sebuah&stryang menunjuk ke dalamStringitu) masih dipakai di sini (borrow later used here).
Bayangkan pelayan yang menulis pesananmu di kertas tempel, lalu membuang kertas itu begitu ia berjalan pergi. Kalau kamu bilang ke temanmu “pesanannya ada di kertas itu”, temanmu tidak akan menemukan apa-apa. Kamu harus menyimpan kertasnya sendiri, atau menulis pesanannya di tempat yang awet.
Bagian help: dari pesannya biasanya sudah jadi jawabannya: buat sebuah variabel (rustc menyebutnya binding) supaya nilainya punya pemilik yang hidup lebih lama.
Kenapa Rust melakukan ini
Section titled “Kenapa Rust melakukan ini”Ini aturan yang sama dengan E0597: sebuah referensi tidak boleh hidup lebih lama dari nilai yang ditunjuknya, karena kalau tidak, ia menjadi dangling reference (referensi menggantung) ke memori yang sudah dibebaskan. Bedanya cuma: di E0597 nilainya punya nama dan mati di sebuah }, sedangkan di E0716 nilainya tidak punya nama dan mati di ;.
Memakai temporary di dalam statement yang sama sama sekali tidak masalah. println!("{}", String::from("hi").as_str()); bisa dikompilasi, karena borrow-nya berakhir sebelum ;. Error-nya hanya muncul kalau borrow-nya masih dibutuhkan di baris berikutnya. Lihat Kapan sebuah borrow berakhir? dan Lifetime.
Ada satu pengecualian yang membantu, namanya temporary lifetime extension (perpanjangan umur nilai sementara). Kalau kamu menaruh & (atau &mut) langsung di depan temporary di dalam let, Rust diam-diam membuat temporary itu tetap hidup selama variabelnya. Itulah kenapa baris-baris ini bisa dikompilasi:
struct Label<'a> { text: &'a str,}
fn make_title() -> String { String::from("Inbox")}
fn main() { let greeting = &String::from("hi"); // ✅ diperpanjang: & langsung di let let queue = &mut vec![1, 2, 3]; // ✅ diperpanjang queue.push(4); let label = Label { text: &make_title() }; // ✅ diperpanjang: & langsung di literal struct println!("{greeting} {queue:?} {}", label.text);}Output:
hi [1, 2, 3, 4] InboxPerpanjangan ini tidak menembus pemanggilan method atau pemanggilan fungsi. String::from("hi").as_str(), make_title().as_str(), vec![1, 2].iter() dan shout(&make_title()) semuanya meminjam temporary di dalam sebuah pemanggilan, jadi temporary-nya tetap mati di ; dan kamu mendapat E0716. Tidak perlu menghafal semua kasusnya: kalau kamu melihat E0716, beri nilainya nama.
Cara memperbaikinya
Section titled “Cara memperbaikinya”Perbaikan 1: Beri nama temporary-nya dengan let
Section titled “Perbaikan 1: Beri nama temporary-nya dengan let”Pilih ini hampir setiap saat. Inilah yang disarankan compiler, dan ini jawaban yang tepat untuk sebagian besar error E0716. Simpan nilainya di variabelnya sendiri dulu, lalu pinjam dari variabel itu di baris berikutnya.
Kasus yang sangat umum adalah memecah sebaris input. read_command() mengembalikan String baru, .trim() dan .split(' ') meminjam darinya, dan Vec<&str> menyimpan borrow-borrow itu:
let parts: Vec<&str> = read_command().trim().split(' ').collect(); // ❌ E0716Rust menunjuk read_command() dan bilang nilainya dibebaskan di akhir statement. Beri nama:
fn read_command() -> String { // Di program sungguhan, ini akan membaca satu baris dari stdin. String::from(" add milk eggs \n")}
fn main() { let line = read_command(); // String-nya sekarang hidup sampai akhir main let parts: Vec<&str> = line.trim().split(' ').collect(); println!("{parts:?}"); println!("perintah: {}, jumlah item: {}", parts[0], parts.len() - 1);}Output:
["add", "milk", "eggs"]perintah: add, jumlah item: 2Kamu akan mengalami hal yang persis sama dengan input sungguhan: lines.next().unwrap().unwrap().trim().split(' ').collect() pada io::stdin().lines() memberi E0716 dengan alasan yang sama. Taruh let line = lines.next().unwrap().unwrap(); di barisnya sendiri dulu. Lihat String.
Perbaikan 2: Simpan nilai milik sendiri, bukan referensi
Section titled “Perbaikan 2: Simpan nilai milik sendiri, bukan referensi”Pilih ini kalau borrow-nya perlu hidup lebih lama dari variabel mana pun yang bisa kamu buat, misalnya di struct yang kamu kirim ke mana-mana, atau di Vec yang kamu isi di dalam perulangan. Sebuah let di dalam perulangan akan di-drop di akhir setiap putaran, dan itu hanya mengubah E0716 menjadi E0597.
Berikut dua versi yang rusak. Struct yang menyimpan &str dari sebuah temporary, dan perulangan yang push borrow dari string-string baru:
let label = Label { text: make_title().as_str() }; // ❌ E0716
let mut names: Vec<&str> = Vec::new();for name in ["ferris", "corro"] { names.push(name.to_uppercase().as_str()); // ❌ E0716}Perbaikannya adalah membiarkan struct dan Vec memiliki string-nya sendiri. Pakai String alih-alih &str, dan buang .as_str()-nya:
struct Label { text: String, // dimiliki sendiri, jadi tidak butuh lifetime}
fn make_title() -> String { String::from("Inbox")}
fn main() { let label = Label { text: make_title() }; // String-nya pindah ke dalam Label println!("Label: {}", label.text);
let mut names: Vec<String> = Vec::new(); for name in ["ferris", "corro"] { names.push(name.to_uppercase()); // Vec memiliki setiap String baru } println!("{names:?}");}Output:
Label: Inbox["FERRIS", "CORRO"]Untuk pemula, field yang dimiliki sendiri (String, Vec<T>) hampir selalu pilihan paling mudah. Struct dengan field &str butuh lifetime dan tidak pernah bisa hidup lebih lama dari data yang dipinjamnya. Lihat Struct yang menyimpan referensi dan Struct.
Perbaikan 3: Susun ulang rantainya
Section titled “Perbaikan 3: Susun ulang rantainya”Pilih ini kalau temporary-nya adalah awal dari sebuah rantai iterator. Baris-baris ini membuat Vec dengan vec![...], meminjamnya dengan .iter(), lalu menyimpan iterator-nya (atau hasilnya) untuk nanti:
let doubled = &mut vec![1, 2, 3].iter().map(|n| n * 10); // ❌ E0716let best = vec![72, 95, 88].iter().max(); // ❌ E0716&mut di depannya tidak membantu: lifetime extension membuat iterator-nya tetap hidup, tapi tidak dengan Vec yang dipinjam oleh .iter(). Ada dua jalan keluar:
- Pakai
into_iter()supaya iterator-nya memilikiVecitu (rustc juga menyarankan ini). Dengan begitu kamu juga tidak butuh&mut, cukuplet mut. - Akhiri rantainya dengan hasil yang dimiliki sendiri, seperti
.copied(),.cloned(),.sum()atau.collect::<Vec<_>>()berisi angka biasa, supaya tidak ada yang masih meminjam temporary-nya setelah;.
fn main() { // Iterator-nya memiliki Vec, jadi tidak ada yang meminjam temporary let mut doubled = vec![1, 2, 3].into_iter().map(|n| n * 10); println!("{:?}", doubled.next()); println!("{:?}", doubled.next());
// .copied() mengubah Option<&i32> menjadi Option<i32>, nilai tanpa borrow let best = vec![72, 95, 88].iter().max().copied(); println!("Terbaik: {best:?}");}Output:
Some(10)Some(20)Terbaik: Some(95)Kalau kamu butuh Vec-nya lagi nanti, kembali ke Perbaikan 1: let scores = vec![72, 95, 88]; lalu scores.iter().max(). Lihat Iterator.
Di mana biasanya kamu melihatnya
Section titled “Di mana biasanya kamu melihatnya”Mengunci Mutex di tengah sebuah rantai. let first = names.lock().unwrap().first(); memberi E0716, karena lock guard-nya adalah temporary. Saat guard itu di-drop, kuncinya dilepas, jadi first tidak boleh terus menunjuk ke dalamnya. Perbaikan: let guard = names.lock().unwrap(); lalu guard.first(), atau salin datanya keluar sekaligus dengan .first().cloned(). Lihat Shared State.
Mengirim &temporary ke fungsi yang mengembalikan referensi. Dengan fn shout(s: &str) -> &str, baris let t = shout(&make_title()); memberi E0716: &str yang dikembalikan meminjam String sementara itu, dan lifetime extension tidak menembus pemanggilan fungsi. Perbaikan: let title = make_title(); lalu shout(&title).
Mengembalikannya dari fungsi. Kalau kamu menulis make_title().as_str() sebagai baris terakhir fungsi yang mengembalikan referensi (seperti -> &'static str), kamu akan mendapat E0515 (“cannot return value referencing temporary value”) sebagai gantinya. Perbaikan: kembalikan String-nya.
Error terkait
Section titled “Error terkait”- E0597: borrowed value does not live long enough: masalah yang sama untuk nilai yang punya nama dan di-drop di sebuah
}. - E0515: cannot return reference to local variable: mengembalikan borrow dari nilai lokal atau temporary dari sebuah fungsi.
- E0106: missing lifetime specifier: field struct seperti
text: &strbutuh lifetime. Sering kali tanda bahwaStringadalah pilihan yang lebih baik. - E0502: mutable borrow while immutably borrowed: error lain tentang “borrow masih dipakai”, kali ini soal mengubah data, bukan men-drop-nya.
- E0382: borrow of moved value: setelah beralih ke nilai milik sendiri, hati-hati memakai nilai setelah kamu memindahkannya.
- Semua error compiler dijelaskan
Kuis singkat
Section titled “Kuis singkat”Kuis singkat
1. Di Rust, apa itu "temporary"?
2. let s = String::from("hi").as_str(); yang diikuti println!("{s}"); memberi E0716. Apa perbaikan yang biasa?
3. Baris mana yang bisa dikompilasi berkat temporary lifetime extension?
Skor: 0 / 3
Bacaan lebih lanjut
Section titled “Bacaan lebih lanjut”- E0716 — Rust error codes index (bahasa Inggris): penjelasan resmi error ini, dengan perbaikan memakai
let. - References and Borrowing — The Rust Book (bahasa Inggris): aturan borrowing, termasuk kenapa referensi tidak pernah boleh menggantung.
- Temporary scopes — The Rust Reference (bahasa Inggris): aturan persis kapan sebuah temporary di-drop.
- Temporary lifetime extension — The Rust Reference (bahasa Inggris): bentuk
letmana saja yang membuat temporary tetap hidup, dengan contoh. - Validating References with Lifetimes — The Rust Book (bahasa Inggris): bagaimana Rust membandingkan berapa lama sebuah nilai hidup dengan berapa lama sebuah referensi dipakai.