Lewati ke konten

Error Rust E0515: cannot return reference to local variable (referensi lokal)

E0515 artinya: fungsimu mencoba mengembalikan referensi (&) ke sesuatu yang ia buat sendiri. Semua yang dibuat oleh sebuah fungsi dibersihkan saat fungsi itu selesai, jadi referensinya akan menunjuk ke ketiadaan. Hampir setiap pemula Rust pernah kena error ini, dan perbaikannya biasanya cukup menghapus satu & lalu mengembalikan nilainya langsung. (Halaman ini bagian dari Penjelasan error compiler Rust.)

error[E0515]: cannot return reference to local variable `greeting`
--> src/main.rs:3:5
|
3 | &greeting // ❌ greeting di-drop saat fungsi selesai
| ^^^^^^^^^ returns a reference to data owned by the current function

Compiler menunjuk &greeting dan bilang: ini mengembalikan referensi ke data yang dimiliki oleh fungsi ini sendiri.

Ini program yang menghasilkan error tersebut:

fn make_greeting(name: &str) -> &str {
let greeting = format!("Hello, {name}!"); // String baru, dimiliki fungsi ini
&greeting // ❌ greeting di-drop saat fungsi selesai
}
fn main() {
let text = make_greeting("Ferris");
println!("{text}");
}

Kamu akan melihat beberapa versi kalimat untuk error yang sama. Artinya sama semua:

  • cannot return reference to local variable kalau kamu menulis &greeting.
  • cannot return value referencing local variable kalau kamu memanggil method yang meminjam, seperti greeting.as_str() atau &scores[2..].
  • cannot return value referencing temporary value kalau datanya tidak pernah disimpan di variabel, seperti text.to_uppercase().as_str().

Setiap versi membawa label “returns a value referencing data owned by the current function” (mengembalikan nilai yang merujuk ke data milik fungsi ini).

greeting adalah variabel lokal: ia dibuat di dalam make_greeting dan dimiliki oleh fungsi itu. Saat fungsinya selesai, semua variabel lokalnya di-drop. &str yang kamu kembalikan akan menunjuk ke dalam String yang sudah tidak ada.

Ini seperti pindah dari kamar kos lalu memberikan kuncinya ke temanmu. Kuncinya memang asli, tapi kamarnya sudah kosong, atau sudah ditempati orang lain. Yang temanmu butuhkan adalah barang-barangnya, bukan kuncinya.

Cara yang sering membawamu ke sini: awalnya kamu mengembalikan greeting (sebuah String) dari fungsi yang tipe kembaliannya &str, lalu mendapat E0308 (tipe tidak cocok), dan compiler menyarankan “consider borrowing here: &greeting”. Saran itu membereskan tipenya, tapi langsung menjerumuskanmu ke E0515. Perbaikan yang sebenarnya adalah mengubah tipe kembaliannya.

Sebuah referensi tidak boleh hidup lebih lama dari nilai yang ditunjuknya. Mengembalikan referensi ke variabel lokal akan menciptakan dangling reference, yaitu pointer ke memori yang sudah dibebaskan. Di C dan C++, kode seperti ini lolos kompilasi lalu crash (atau lebih parah, diam-diam mengembalikan data sampah). Rust menangkapnya saat kompilasi.

Referensi yang dikembalikan harus meminjam dari sesuatu yang hidup di luar fungsi: parameter yang diberikan pemanggil, field dari self, atau nilai 'static seperti string literal. Variabel lokal milik fungsi itu sendiri tidak memenuhi syarat. Karena itulah anotasi lifetime tidak bisa memperbaiki E0515: 'a hanya menjelaskan berapa lama data hidup, ia tidak bisa membuat variabel lokal hidup lebih lama. Lihat Ownership (kepemilikan) dan Borrowing (peminjaman).

Perbaikan 1: Kembalikan nilai milik sendiri (owned)

Section titled “Perbaikan 1: Kembalikan nilai milik sendiri (owned)”

Pilih ini kalau fungsinya membuat datanya. Ini jawaban yang tepat di sebagian besar kasus. Kembalikan String alih-alih &str (atau Vec<T> alih-alih &[T]) dan hapus &-nya. Nilainya dipindahkan (move) ke pemanggil, yang kemudian memilikinya:

fn make_greeting(name: &str) -> String {
let greeting = format!("Halo, {name}!");
greeting // ✅ serahkan seluruh String ke pemanggil
}
fn top_scores() -> Vec<u32> {
let mut scores = vec![42, 97, 63, 88];
scores.sort();
scores.split_off(2) // dua nilai tertinggi, sebagai Vec baru
}
fn main() {
let text = make_greeting("Ferris");
println!("{text}");
println!("{:?}", top_scores());
}

Output:

Halo, Ferris!
[88, 97]
▶ Coba sendiri

Jangan khawatir soal biaya: mengembalikan String atau Vec hanya memindahkan pointer kecil, panjang, dan kapasitasnya. Teksnya sendiri tidak disalin. Kamu juga bisa langsung menulis format!("Halo, {name}!") sebagai baris terakhir.

Perbaikan 2: Terima datanya sebagai parameter dan kembalikan referensi ke dalamnya

Section titled “Perbaikan 2: Terima datanya sebagai parameter dan kembalikan referensi ke dalamnya”

Pilih ini kalau pemanggil sudah memiliki datanya dan kamu hanya ingin menunjuk ke sebagiannya. Kalau fungsinya bekerja pada parameter pinjaman, referensi yang dikembalikan meminjam dari parameter itu, yang hidup di luar fungsi:

fn top_two(scores: &mut [u32]) -> &[u32] {
scores.sort();
&scores[scores.len() - 2..] // ✅ menunjuk ke data milik pemanggil
}
fn main() {
let mut scores = vec![42, 97, 63, 88]; // dimiliki oleh main
let best = top_two(&mut scores);
println!("{best:?}");
}

Output:

[88, 97]
▶ Coba sendiri

Dengan satu parameter referensi, Rust mengisi lifetime-nya untukmu. Dengan dua atau lebih, kamu mungkin perlu 'a (lihat E0106). Cara ini hanya berhasil kalau kamu mengembalikan potongan dari input. Nilai yang benar-benar baru yang dibuat dari input (to_uppercase(), format!, trim().to_string()) adalah variabel lokal lagi, jadi kembali ke Perbaikan 1. Lihat Slice.

Perbaikan 3: Kembalikan &'static str untuk string literal

Section titled “Perbaikan 3: Kembalikan &'static str untuk string literal”

Pilih ini kalau setiap kemungkinan hasilnya adalah teks tetap yang ditulis di kodemu. String literal disimpan di dalam program itu sendiri dan hidup selamanya, jadi mengembalikan referensi ke literal selalu aman:

fn status_text(code: u16) -> &'static str {
match code {
200 => "OK",
404 => "Not Found",
500 => "Internal Server Error",
_ => "Unknown",
}
}
fn main() {
for code in [200, 404, 418] {
println!("{code}: {}", status_text(code));
}
}

Output:

200: OK
404: Not Found
418: Unknown
▶ Coba sendiri

Jangan mencoba “memperbaiki” hasil format! dengan menulis &'static str di signature. Kamu hanya akan mendapat E0515 lagi, karena String-nya tetap variabel lokal.

Perbaikan 4: Pakai Cow kalau kadang meminjam dan kadang membuat baru

Section titled “Perbaikan 4: Pakai Cow kalau kadang meminjam dan kadang membuat baru”

Pilih ini kalau fungsinya biasanya mengembalikan input-nya tanpa perubahan, tapi kadang harus membuat string baru. Cow (clone-on-write) bisa berisi &str pinjaman atau String milik sendiri, jadi kamu hanya mengalokasikan memori kalau memang perlu:

use std::borrow::Cow;
fn safe_file_name(name: &str) -> Cow<'_, str> {
if name.contains(' ') {
Cow::Owned(name.replace(' ', "_")) // String baru hanya kalau perlu
} else {
Cow::Borrowed(name) // tidak ada salinan sama sekali
}
}
fn main() {
for name in ["report.txt", "my notes.txt"] {
let clean = safe_file_name(name);
let kind = match clean {
Cow::Borrowed(_) => "dipinjam",
Cow::Owned(_) => "milik sendiri",
};
println!("{clean} ({kind})");
}
}

Output:

report.txt (dipinjam)
my_notes.txt (milik sendiri)
▶ Coba sendiri

Cow adalah optimasi. Kalau kamu tidak yakin membutuhkannya, kembalikan String saja (Perbaikan 1). Selengkapnya di Weak dan Cow.

Mengembalikan s.as_str() dari String lokal. greeting.as_str() meminjam greeting, jadi gagal dengan cara yang sama seperti &greeting, dengan kalimat “cannot return value referencing local variable”. Perbaikan: kembalikan greeting dan jadikan tipe kembaliannya String.

.map() yang membuat string baru. Kelihatannya tidak berbahaya, tapi setiap to_lowercase() membuat String sementara yang langsung di-drop setelah .as_str():

fn tags(input: &str) -> Vec<&str> {
input.split(',').map(|t| t.trim().to_lowercase().as_str()).collect()
// ❌ cannot return value referencing temporary value
}

Perbaikan: kembalikan Vec<String> dan hapus .as_str(): .map(|t| t.trim().to_lowercase()).collect(). (Tanpa to_lowercase(), t.trim() adalah slice dari input, jadi Vec<&str> akan berhasil. Lihat Iterator.)

Method yang memformat field. fn full_name(&self) -> &str { &format!("{} {}", self.first, self.last) } memberi “cannot return reference to temporary value”. Perbaikan: -> String. Mengembalikan &self.first tidak masalah, karena data itu hidup di dalam struct, bukan di dalam method. Lihat Struct.

Fungsi tanpa parameter referensi. fn make_greeting() -> &String gagal lebih awal, dengan E0106, karena tidak ada apa pun yang bisa dipinjam. Perbaikannya sama: kembalikan String.

Kuis singkat

1. Sebuah fungsi membuat pesan dengan format! lalu mengembalikan &message. Apa perbaikan yang biasanya tepat?

2. Fungsi mana yang bisa dikompilasi?

3. Kapan Cow<str> cocok sebagai tipe kembalian?

Skor: 0 / 3