Error Rust E0106: missing lifetime specifier (lifetime tidak disebutkan)
E0106 artinya: kamu menulis sebuah referensi (&) di tipe kembalian fungsi atau di dalam struct, dan Rust tidak bisa tahu referensi itu meminjam dari mana. Ini bukan bug di logikamu. Compiler cuma butuh satu kata tambahan darimu, atau sedikit perubahan rencana. Hampir semua orang bertemu error ini di minggu pertama, dan perbaikannya biasanya cuma satu baris. (Halaman ini bagian dari Penjelasan error compiler Rust.)
Pesan error-nya
Section titled “Pesan error-nya”error[E0106]: missing lifetime specifier --> src/main.rs:1:32 |1 | fn longer(a: &str, b: &str) -> &str { | ---- ---- ^ expected named lifetime parameter | = help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `a` or `b`help: consider introducing a named lifetime parameter |1 | fn longer<'a>(a: &'a str, b: &'a str) -> &'a str { | ++++ ++ ++ ++Compiler bilang tipe kembaliannya berisi nilai pinjaman, tapi signature-nya tidak menyebut apakah nilai itu dipinjam dari a atau dari b. Ia juga langsung menyarankan perbaikannya: tambahkan parameter lifetime bernama 'a.
Ini program yang menghasilkan error tersebut:
fn longer(a: &str, b: &str) -> &str { if a.len() >= b.len() { a } else { b }}
fn main() { println!("{}", longer("apple", "fig"));}Apa artinya
Section titled “Apa artinya”Referensi adalah sebuah pinjaman (borrowing, peminjaman): ia menunjuk ke data yang dimiliki pihak lain. Setiap kali fungsi mengembalikan referensi, Rust perlu tahu data milik siapa yang ditunjuknya, supaya bisa memastikan data itu tetap hidup selama pemanggil memakai hasilnya.
Lihat tiga tanda di bawah baris 1. Compiler menggarisbawahi a dan b dengan ----, dan tipe kembaliannya dengan ^. Maksudnya: “kamu mengembalikan &str pinjaman, dan itu bisa berasal dari a atau dari b. Yang mana?” Compiler tidak mau menebak.
Bayangkan kartu perpustakaan. Kalau teman memberimu buku dan bilang “ini pinjaman”, kamu pasti ingin tahu dari siapa, supaya tahu kapan buku itu harus dikembalikan. Lifetime adalah label pada pinjaman itu.
Kenapa Rust melakukan ini
Section titled “Kenapa Rust melakukan ini”Rust menjamin sebuah referensi tidak akan pernah hidup lebih lama dari data yang ditunjuknya (tidak ada “dangling pointer”). Untuk memeriksa itu saat fungsi dipanggil, compiler hanya melihat signature fungsinya, bukan isinya. Jadi signature itu sendiri harus menyebutkan dari mana referensi yang dikembalikan berasal.
Biasanya kamu tidak perlu menulis lifetime sama sekali, karena Rust mengisinya sendiri dengan tiga aturan elision sederhana:
- Setiap parameter referensi mendapat lifetime-nya sendiri.
- Kalau ada tepat satu parameter referensi, referensi yang dikembalikan meminjam dari parameter itu.
- Kalau fungsinya method dengan
&selfatau&mut self, referensi yang dikembalikan meminjam dariself.
E0106 muncul kalau tidak satu pun aturan ini memberi jawaban: ada dua parameter referensi atau lebih (tanpa self), tidak ada parameter referensi sama sekali, atau ada referensi yang disimpan di struct. Pelajaran Lifetime membahas ini lebih dalam, dan Borrowing menjelaskan referensi itu sendiri.
Cara memperbaikinya
Section titled “Cara memperbaikinya”Perbaikan 1: Tambahkan lifetime bernama ('a)
Section titled “Perbaikan 1: Tambahkan lifetime bernama ('a)”Pilih ini kalau fungsinya memang mengembalikan bagian dari salah satu input-nya. 'a hanyalah sebuah nama. Menuliskannya di input dan output berarti “hasilnya hidup selama input-input ini hidup”:
// 'a: &str yang dikembalikan meminjam dari a atau b, jadi ia hidup selama keduanya hidup.fn longer<'a>(a: &'a str, b: &'a str) -> &'a str { if a.len() >= b.len() { a } else { b }}
fn main() { let first = String::from("apple"); let second = String::from("fig"); println!("Kata yang lebih panjang: {}", longer(&first, &second));}Output:
Kata yang lebih panjang: appleKalau hasilnya selalu berasal dari satu parameter saja, pasang 'a hanya di parameter itu: fn lookup<'a>(map: &'a HashMap<String, String>, key: &str) -> Option<&'a str>. Saran compiler memasang 'a di semua tempat. Itu berhasil, tapi lebih ketat dari yang diperlukan.
Perbaikan 2: Kembalikan String milik sendiri
Section titled “Perbaikan 2: Kembalikan String milik sendiri”Pilih ini kalau fungsinya membuat teks baru, atau kalau kamu memang tidak ingin pemanggil terikat pada sebuah pinjaman. String yang owned (dimiliki sendiri) menjadi milik pemanggil, jadi tidak ada yang perlu dilacak:
fn greeting(name: &str, lang: &str) -> String { if lang == "id" { format!("Halo, {name}!") } else { format!("Hello, {name}!") }}
fn main() { println!("{}", greeting("Ana", "id")); println!("{}", greeting("Sam", "en"));}Output:
Halo, Ana!Hello, Sam!Ini memang butuh satu alokasi kecil. Untuk kebanyakan program, itu sama sekali tidak masalah. Jangan merasa bersalah memakainya.
Perbaikan 3: Simpan data owned di struct-mu
Section titled “Perbaikan 3: Simpan data owned di struct-mu”Field struct seperti title: &str memberi error yang sama:
error[E0106]: missing lifetime specifier --> src/main.rs:2:12 |2 | title: &str, | ^ expected named lifetime parameterUntuk struct, perbaikan terbaik hampir selalu adalah memiliki datanya sendiri: pakai String alih-alih &str, dan Vec<T> alih-alih &[T]. Dengan begitu struct-nya bisa hidup selama yang kamu mau:
#[derive(Debug)]struct Book { title: String, // owned, bukan &str pages: u32,}
fn main() { let book = Book { title: String::from("Dune"), pages: 412 }; println!("{} ({} halaman)", book.title, book.pages);}Output:
Dune (412 halaman)Beri struct sebuah lifetime hanya kalau struct itu adalah tampilan sementara ke data yang hidup di tempat lain, misalnya parser yang membaca teks besar. Dalam kasus itu, tulis <'a> di struct dan di field-nya:
struct Excerpt<'a> { text: &'a str, // meminjam dari novel di bawah}
fn main() { let novel = String::from("Call me Ishmael. Some years ago..."); let first = novel.split('.').next().unwrap(); let excerpt = Excerpt { text: first }; println!("Kutipan: {}", excerpt.text);}Output:
Kutipan: Call me IshmaelPerbaikan 4: Pakai 'static untuk string literal
Section titled “Perbaikan 4: Pakai 'static untuk string literal”Kalau fungsinya tidak punya parameter referensi sama sekali, error-nya bilang “there is no value for it to be borrowed from” (tidak ada nilai yang bisa dipinjam):
error[E0106]: missing lifetime specifier --> src/main.rs:1:22 |1 | fn default_name() -> &str { | ^ expected named lifetime parameter | = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed fromKalau kamu hanya mengembalikan string literal seperti "OK", tulis &'static str. Literal sudah tertanam di dalam programmu, jadi ia hidup selama program berjalan:
fn status_text(code: u16) -> &'static str { match code { 200 => "OK", 404 => "Not Found", _ => "Unknown", }}
fn main() { println!("200 artinya {}", status_text(200)); println!("404 artinya {}", status_text(404));}Output:
200 artinya OK404 artinya Not FoundPakai 'static hanya untuk literal dan konstanta sungguhan. Kalau teksnya dibuat saat program berjalan (dengan format!, atau dibaca dari file), 'static tidak akan menolong: kembalikan String (Perbaikan 2).
Di mana biasanya kamu melihatnya
Section titled “Di mana biasanya kamu melihatnya”- Fungsi pembantu dengan dua input, seperti
fn lookup(map: &HashMap<String, String>, key: &str) -> Option<&str>. Perbaikan: ikat output-nya hanya kemapdengan'a(Perbaikan 1). - Struct yang menyimpan teks, seperti
User { name: &str }yang dibuat dari input hasil parsing. Perbaikan: pakai fieldString(Perbaikan 3). - Fungsi yang mengembalikan pesan tetap, seperti
fn error_label() -> &str. Perbaikan:&'static str(Perbaikan 4).
Kabar baiknya ada di method: fn name(&self) -> &str sama sekali tidak butuh lifetime, berkat aturan nomor 3.
Error terkait
Section titled “Error terkait”- E0382: borrow of moved value: error ownership lain, tentang memakai nilai setelah kamu memberikannya ke tempat lain.
- E0502: mutable borrow while immutably borrowed: borrow checker bekerja setelah lifetime-mu beres.
- E0499: two mutable borrows: dua
&mutke data yang sama pada saat bersamaan. - E0308: mismatched types: misalnya mengembalikan
Stringdari fungsi yang tipe kembaliannya&str. E0515: cannot return reference to local variable: kamu mencoba Perbaikan 1 pada data yang dibuat di dalam fungsi. KembalikanStringmilik sendiri saja (Perbaikan 2).- Semua error compiler dijelaskan
Kuis singkat
Section titled “Kuis singkat”Kuis singkat
1. Kenapa fn first_word(s: &str) -> &str bisa dikompilasi tanpa lifetime apa pun?
2. Struct Config { path: &str } milikmu memberi E0106. Apa perbaikan yang biasanya paling baik?
3. Sebuah fungsi membuat pesan dengan format! lalu mengembalikannya. Tipe kembalian apa yang seharusnya dipakai?
Skor: 0 / 3
Bacaan lebih lanjut
Section titled “Bacaan lebih lanjut”- E0106 — Rust error codes index (bahasa Inggris): penjelasan resmi error ini.
- Validating References with Lifetimes — The Rust Book (bahasa Inggris): cerita lengkap tentang lifetime, termasuk contoh
longestdan struct yang berisi referensi. - Lifetime elision — The Rust Reference (bahasa Inggris): aturan persis yang dipakai Rust untuk mengisi lifetime untukmu.
- Elision — Rust by Example (bahasa Inggris): contoh singkat kapan kamu boleh tidak menulis lifetime.
- Lifetimes — Rust by Example (bahasa Inggris): contoh yang bisa dijalankan tentang lifetime eksplisit di fungsi, method, dan struct.