Rust'taki üç yaygın lifetime hatası farklı şeyler söyler. error[E0106]: missing lifetime specifier, bir fonksiyon imzasında, struct'ta veya tip takma adında referansın hangi değere bağlı olduğunu derleyicinin çıkaramadığını bildirir; çözüm 'a gibi bir lifetime parametresi yazmak veya sahipli tip döndürmektir. error[E0597]: ... does not live long enough, referansı hâlâ kullanılan bir değerin daha önce bırakıldığını söyler; çözüm değeri daha geniş bir kapsamda tanımlamaktır. error[E0515]: cannot return reference to local variable ise fonksiyon bitince yok olacak yerel bir değere referans döndürmeye çalıştığınızı gösterir; çözüm referans yerine değerin kendisini, örneğin String döndürmektir.
Bu yazıda bu üç hatayı ve yakın akrabaları E0716'yı Rust 1.99.1'in gerçek mesajlarıyla inceliyoruz. Lifetime sözdiziminin kendisi ve generic lifetime parametreleri Rust lifetime yazısında ayrıntılı anlatılıyor; burada hatayı gördüğünüzde nasıl teşhis edeceğinize odaklanıyoruz. Aynı anda ödünç alma çakışmaları için E0499 ve E0502, taşınmış değer hataları için E0382 yazılarına bakabilirsiniz.
Önce tek bir cümle: lifetime etiketi ömür uzatmaz
Bu hataları çözerken en çok yapılan yanlış, lifetime etiketlerinin değerleri daha uzun yaşatacağını sanmaktır. The Rust Programming Language kitabı açıkça söyler: lifetime etiketleri hiçbir referansın ne kadar yaşadığını değiştirmez; yalnızca birden çok referansın ömürleri arasındaki ilişkiyi tarif eder. Derleyicinin ödünç alma denetleyicisi bu tarifleri kodun gerçek kapsamlarıyla karşılaştırır. Tarif eksikse E0106, tarif gerçekle çelişiyorsa E0597 veya E0515 alırsınız. Dolayısıyla E0106'da etiket yazmak çözüm olabilir, ama E0597 ve E0515'te etiket eklemek neredeyse hiçbir zaman işe yaramaz; çünkü sorun tarifte değil, değerin gerçekten erken yok olmasındadır.
E0106: missing lifetime specifier

Fonksiyon imzasında
İki referans alıp bir referans döndüren bir fonksiyon, en sık görülen E0106 kaynağıdır:
fn en_uzun(x: &str, y: &str) -> &str {
if x.len() > y.len() { x } else { y }
}
fn main() {
println!("{}", en_uzun("elma", "karpuz"));
}
// error[E0106]: missing lifetime specifierRust 1.99.1'in yardım satırı sorunu tam olarak tarif eder: dönüş tipi ödünç alınmış bir değer içeriyor, ama imza bu değerin x mi yoksa y mi olduğunu söylemiyor. Derleyici gövdeye bakarak karar vermez; çağıran kodun yalnızca imzaya güvenebilmesi gerekir. Önerilen çözüm, iki girdiyi ve çıktıyı aynı 'a parametresine bağlamaktır. Bu imza şunu söyler: dönen referans, iki girdiden kısa yaşayanı kadar geçerlidir.
fn en_uzun<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() { x } else { y }
}
// Tek referans girişi olduğunda elision kuralı yeter, etiket gerekmez
fn ilk_kelime(s: &str) -> &str {
s.split(' ').next().unwrap_or("")
}
fn main() {
println!("{}", en_uzun("elma", "karpuz"));
println!("{}", ilk_kelime("merhaba dünya"));
}
// çıktı: karpuz
// çıktı: merhabailk_kelime fonksiyonunda etiket gerekmemesinin nedeni lifetime elision kurallarıdır. Kitap üç kural sayar. Birincisi: referans olan her parametreye ayrı bir lifetime parametresi atanır. İkincisi: tam olarak bir girdi lifetime'ı varsa, o lifetime tüm çıktı referanslarına atanır. Üçüncüsü: birden çok girdi lifetime'ı varsa ama biri &self veya &mut self ise, self parametresinin lifetime'ı çıktılara atanır. Derleyici bu üç kuralı uyguladıktan sonra hâlâ lifetime'ı belirsiz bir çıktı referansı kalırsa E0106 verir. en_uzun fonksiyonunda iki girdi olduğu ve fonksiyon bir metot olmadığı için ikinci ve üçüncü kural uygulanamaz.
| İmza | Hangi kural | Sonuç |
|---|---|---|
| fn f(x: &str) -> &str | İkinci kural | Derlenir, çıktı x ile bağlı |
| fn f(x: &str, y: &str) -> &str | Hiçbiri yetmez | E0106 |
| fn f(&self, y: &str) -> &str | Üçüncü kural | Derlenir, çıktı self ile bağlı |
| fn f() -> &str | Girdi yok | E0106; 'static veya String önerilir |
| fn f<'a>(x: &'a str, y: &str) -> &str | y için ayrı lifetime çıkarılır | E0106, çıktı yine belirsiz |
Son satır hata kodu dizinindeki bir örnektir: yalnızca bir parametreye etiket yazmak yetmez, çünkü diğer parametre de kendi ayrı lifetime'ını alır ve çıktı yine iki aday arasında kalır.
Girdi hiç yokken
Hiç referans parametresi almayan bir fonksiyonun referans döndürmesi için ödünç alacağı bir şey yoktur:
fn varsayilan_mesaj() -> &str {
"bilinmeyen hata"
}
fn main() {
println!("{}", varsayilan_mesaj());
}
// error[E0106]: missing lifetime specifierRust 1.99.1 burada iki öneri sunar: 'static lifetime kullanmak, ancak bunun bir const veya static değerden ödünç alınmadıkça olağan olmadığını belirterek; ya da büyük olasılıkla daha doğru olan sahipli bir değer, yani String döndürmek. Bu örnekte döndürülen değer bir dizgi sabiti olduğu için 'static doğrudur; kitaba göre tüm dizgi sabitleri programın ikili dosyasında saklanır ve 'static lifetime'a sahiptir:
fn varsayilan_mesaj() -> &'static str {
"bilinmeyen hata"
}
fn main() {
println!("{}", varsayilan_mesaj());
}
// çıktı: bilinmeyen hataStruct, enum ve tip takma adlarında
Struct alanlarında elision yoktur: referans tutan bir alan her zaman açık bir lifetime ister. Hata kodu dizini aynı kuralın enum varyantları ve type takma adları için de geçerli olduğunu gösterir.
struct Alinti {
metin: &str,
}
fn main() {
let a = Alinti { metin: "kısa bir alıntı" };
println!("{}", a.metin);
}
// error[E0106]: missing lifetime specifierİki çözüm vardır ve hangisinin doğru olduğu tasarıma bağlıdır. Struct gerçekten başka bir yerdeki metnin bir parçasını göstermek içinse lifetime parametresi eklenir; bu durumda struct, gösterdiği metinden uzun yaşayamaz. Struct verinin sahibi olacaksa alanı String yapmak hem etiketi hem de ömür kısıtını ortadan kaldırır:
struct Alinti<'a> {
metin: &'a str,
}
// Sahipli alternatif: yaşam süresi etiketi gerekmez
struct AlintiSahipli {
metin: String,
}
fn main() {
let kaynak = String::from("Uzun bir paragraf. İkinci cümle.");
let ilk = kaynak.split('.').next().unwrap();
let a = Alinti { metin: ilk };
let b = AlintiSahipli { metin: ilk.to_string() };
println!("{} | {}", a.metin, b.metin);
}
// çıktı: Uzun bir paragraf | Uzun bir paragrafBir struct'ın metotlarında ise üçüncü elision kuralı sayesinde çoğu zaman etiket yazmanız gerekmez. Aşağıdaki metot &self ile birlikte ikinci bir referans parametresi aldığı halde derlenir, çünkü dönüş self ile bağlanır:
struct Ayristirici<'a> {
girdi: &'a str,
}
impl<'a> Ayristirici<'a> {
// &self ve başka referans varken dönüş, elision ile self'e bağlanır
fn ilk_satir(&self, _ayrac: &str) -> &str {
self.girdi.lines().next().unwrap_or("")
}
}
fn main() {
let metin = String::from("başlık\ngövde");
let a = Ayristirici { girdi: &metin };
println!("{}", a.ilk_satir("\n"));
}
// çıktı: başlıkE0597: does not live long enough
E0597, bir değer bırakıldığı halde ona olan referansın hâlâ kullanılacağı durumda çıkar. Kitabın sarkan referans örneği bunun en sade biçimidir:
fn main() {
let r;
{
let x = 5;
r = &x;
}
println!("r: {r}");
}
// error[E0597]: `x` does not live long enoughMesajın dört etiketi hikâyeyi sırayla anlatır: binding `x` declared here değişkenin tanımlandığı yeri, borrowed value does not live long enough ödünç alındığı yeri, `x` dropped here while still borrowed iç bloğun kapandığı yeri ve borrow later used here referansın sonradan kullanıldığı yeri gösterir. x iç bloğun sonunda bırakılıyor, ama r bloğun dışında hâlâ okunmak isteniyor. Çözüm, değeri referanstan en az onun kadar uzun yaşayacak bir kapsama taşımaktır:
fn main() {
let x = 5;
let r;
{
r = &x;
}
println!("r: {r}");
}
// çıktı: r: 5Lifetime parametreli fonksiyonlarla E0597
Aynı hata, imzası doğru yazılmış bir fonksiyonu yanlış kapsamlarla çağırdığınızda da çıkar. en_uzun imzası sonucun iki girdiden kısa yaşayanı kadar geçerli olduğunu söylüyordu. s2 iç blokta bırakıldığı için sonuç da en fazla o bloğun sonuna kadar geçerlidir:
fn en_uzun<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() { x } else { y }
}
fn main() {
let s1 = String::from("uzun bir metin");
let sonuc;
{
let s2 = String::from("kısa");
sonuc = en_uzun(s1.as_str(), s2.as_str());
}
println!("en uzun: {sonuc}");
}
// error[E0597]: `s2` does not live long enoughBu örnekte sonucun aslında s1 olacağını siz bilirsiniz, ama derleyici çalışma anındaki değerlere değil, imzadaki ilişkiye bakar. İmza, sonucun s2 olabileceğini söylediği sürece hata doğru bir uyarıdır. s2 değişkenini dış kapsama aldığımızda kod derlendi ve en uzun: uzun bir metin yazdı. Başka bir seçenek, sonucu iç blokta kullanmak veya sonucun kopyasını almaktır (to_string()).
E0515: yerel değere referans döndürmek

Fonksiyonun içinde oluşturduğunuz bir değer, fonksiyon bittiğinde bırakılır. Ona referans döndürmek, çağırana artık var olmayan bir şeyi göstermek olurdu:
fn selam(ad: &str) -> &str {
let metin = format!("Merhaba, {ad}!");
&metin
}
fn main() {
println!("{}", selam("Deniz"));
}
// error[E0515]: cannot return reference to local variable `metin`Burada lifetime etiketi eklemek işe yaramaz. İmzayı fn selam<'a>(ad: &'a str) -> &'a str yaptığımızda aynı E0515 hatasını aldık, çünkü metin hâlâ fonksiyonun sonunda yok oluyor. Doğru çözüm, sahipliği çağırana devretmektir; hata kodu dizini de yerel veriyi sahiplenen bir değer döndürmeyi önerir:
fn selam(ad: &str) -> String {
format!("Merhaba, {ad}!")
}
fn main() {
println!("{}", selam("Deniz"));
}
// çıktı: Merhaba, Deniz!Aynı hata bir referans içeren tip döndürdüğünüzde de çıkar. Yerel bir vektör üzerinde iter() çağırıp iterator'ü döndürmek, vektöre referans tutan bir değer döndürmek demektir; mesaj bu kez cannot return value referencing local variable `v` olur:
fn cift_sayilar() -> std::slice::Iter<'static, i32> {
let v = vec![2, 4, 6];
v.iter()
}
fn main() {
for n in cift_sayilar() {
println!("{n}");
}
}
// error[E0515]: cannot return value referencing local variable `v`Çözüm, vektörü sahiplenen iterator'ü döndürmektir. into_iter() vektörü tüketir ve döndürülen IntoIter<i32> verinin sahibi olur:
fn cift_sayilar() -> std::vec::IntoIter<i32> {
let v = vec![2, 4, 6];
v.into_iter()
}
fn main() {
for n in cift_sayilar() {
println!("{n}");
}
}
// çıktı: 2
// çıktı: 4
// çıktı: 6Mini örnek: dosyadan okunan metne referans döndürmek
E0515 en çok, bir fonksiyonun kendi okuduğu veya ürettiği veriden bir parça döndürmek istediği gerçek kodlarda çıkar. Aşağıdaki fonksiyon bir ayar dosyasını okuyup ilk satırını döndürmeye çalışıyor. lines() ve next() zinciri icerik değişkenine referans tutan bir &str üretir, ama icerik fonksiyon bitince bırakılır:
use std::fs;
fn sunucu_adi(yol: &str) -> &str {
let icerik = fs::read_to_string(yol).unwrap_or_default();
icerik.lines().next().unwrap_or("")
}
fn main() {
println!("{}", sunucu_adi("ayar.txt"));
}
// error[E0515]: cannot return value referencing local variable `icerik`Dikkat ederseniz bu imza E0106 vermedi; tek bir referans parametresi olduğu için elision kuralı dönüşü yol parametresine bağladı. Derleyici imzayı kabul etti ama gövdenin bu sözü tutmadığını gördü: dönen dilim yol değil icerik içinden geliyor. Burada iki temiz tasarım var. Birincisinde metnin sahibi çağırandır; fonksiyon yalnızca ödünç aldığı metinden bir parça döndürür ve elision kuralı bu kez doğru ilişkiyi kurar. İkincisinde fonksiyon sonucu sahipli bir String olarak döndürür:
// Tasarım 1: metnin sahibi çağıran; fonksiyon yalnızca ödünç alır
fn sunucu_adi(icerik: &str) -> &str {
icerik.lines().next().unwrap_or("")
}
// Tasarım 2: fonksiyon kendi okuduğu veriden sahipli bir değer döndürür
fn sunucu_adi_sahipli(icerik: String) -> String {
icerik.lines().next().unwrap_or("").to_string()
}
fn main() {
let icerik = String::from("web01\nport=8080");
println!("{}", sunucu_adi(&icerik));
println!("{}", sunucu_adi_sahipli(icerik));
}
// çıktı: web01
// çıktı: web01Birinci tasarım kopya yapmaz ve çağıran metni başka işler için de kullanabilir; ikincisi daha basit bir arayüz sunar ama küçük bir kopya üretir. Dosya okuma ve hata yönetimini birlikte ele almak için std::fs ile dosya okuma ve yazma yazısına bakabilirsiniz.
Yakın akraba: E0716 geçici değer
Bir fonksiyon çağrısının sonucunu doğrudan ödünç aldığınızda derleyici bir geçici değer oluşturur. Geçici değerler normalde içinde bulundukları deyimin sonunda bırakılır. Ondan türetilen referansı sonraki satırda kullanmak E0716 verir:
fn ayni(x: &String) -> &str {
x.as_str()
}
fn main() {
let p = ayni(&String::from("geçici"));
println!("{p}");
}
// error[E0716]: temporary value dropped while borrowedDerleyici let ile daha uzun yaşayan bir değer oluşturmayı önerir. Değere bir ad verdiğinizde o değer bloğun sonuna kadar yaşar:
fn ayni(x: &String) -> &str {
x.as_str()
}
fn main() {
let deger = String::from("kalıcı");
let p = ayni(°er);
println!("{p}");
}
// çıktı: kalıcıHata kodu dizini bir ayrıntıyı daha not eder: let x = &foo(); gibi, referansın doğrudan bir değişkene bağlandığı basit durumlarda derleyici geçici değerin ömrünü bloğun sonuna kadar kendisi uzatır. Sorun, geçici değer bir fonksiyon çağrısının içinden geçtiğinde ortaya çıkar.
'static ne zaman doğru çözüm?
Derleyici bazen 'static önerir ve bu öneriyi her yere uygulamak cazip gelir. Kitap bu konuda nettir: 'static yazmadan önce referansın gerçekten programın tüm ömrü boyunca yaşayıp yaşamadığını düşünün. Kitaba göre 'static öneren hata mesajlarının çoğu, sarkan bir referans oluşturma girişiminden veya mevcut lifetime'ların uyuşmamasından kaynaklanır ve çözüm bu sorunları düzeltmektir. Dizgi sabitleri, static değişkenler ve const değerler için 'static doğrudur; çalışma anında oluşturulan bir String içinse genellikle sahipli tip döndürmek gerekir.
Teşhis tablosu

| Mesaj | Kod | Anlamı | Genellikle çözüm |
|---|---|---|---|
| missing lifetime specifier | E0106 | Çıktı referansının kaynağı belirsiz | Lifetime parametresi, 'static veya sahipli tip |
| does not live long enough | E0597 | Değer, referansı kullanılırken bırakılmış | Değeri daha geniş kapsama taşımak |
| cannot return reference to local variable | E0515 | Fonksiyon bitince yok olacak değere referans | String, Vec gibi sahipli değer döndürmek |
| cannot return value referencing local variable | E0515 | Döndürülen değer yerel veriye referans tutuyor | Sahiplenen tip, ör. into_iter() |
| temporary value dropped while borrowed | E0716 | Geçici değer deyim sonunda bırakılıyor | Değeri let ile adlandırmak |
Sık yapılan hatalar
- E0597 veya E0515'i etiketle çözmeye çalışmak: Etiketler ömür uzatmaz. Değerin gerçekten nerede yok olduğuna bakın.
- Her E0106'ya 'static yazmak: Bu, çağıranları yalnızca statik veriyle çalışmaya zorlar ve çoğu zaman bir sonraki satırda yeni bir hata doğurur.
- Gereksiz yere referans tutan struct tasarlamak: Struct verinin sahibi olabiliyorsa
StringveyaVec<T>alanları kodu sadeleştirir. Referans tutan struct'lar, kısa ömürlü ayrıştırıcılar gibi gerçekten ödünç alma gereken durumlar içindir. - Tek parametreye etiket yazıp yeterli sanmak: Birden çok referans parametresi varsa çıktının hangisiyle ilişkili olduğu açıkça yazılmalıdır.
- Çalışma anındaki değere güvenmek: Derleyici
en_uzunörneğinde hangi dalın seçileceğine bakmaz; yalnızca imzadaki ilişkiyi denetler.
Sık Sorulan Sorular
Lifetime etiketi programın davranışını değiştirir mi?
Hayır. Kitaba göre etiketler hiçbir referansın ömrünü değiştirmez; yalnızca derleyiciye referanslar arasındaki ilişkiyi anlatır. Denetimi derleme sırasında ödünç alma denetleyicisi yapar ve kapsamları karşılaştırarak tüm ödünç almaların geçerli olup olmadığına karar verir.
'a adının bir anlamı var mı?
Hayır. Lifetime parametreleri kesme işaretiyle başlayan herhangi bir ad olabilir; kitap genellikle kısa ve küçük harfli adları, çoğunlukla 'a adını kullanır. Önemli olan, aynı adın hangi referansları birbirine bağladığıdır.
E0597 ile E0505 arasındaki fark nedir?
E0597, ödünç alınmış değerin kapsamdan çıkarak bırakılmasıdır. E0505 ise ödünç alınmış değerin başka bir yere taşınmasıdır. İkisinde de referans geçersiz kalacağı için derleyici durdurur; E0505 için E0382 ve move hataları yazısına bakın.
Derleyicinin önerisini doğrudan uygulamalı mıyım?
Öneriler çoğu zaman doğru yönü gösterir, ama birden fazla seçenek sunulduğunda tasarımınıza uygun olanı seçmelisiniz. Örneğin E0106 için hem 'static hem String önerildiğinde, değer çalışma anında üretiliyorsa doğru seçim String olur.
Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.
İlgili içerikler: Rust öğrenme rehberi · Rust lifetime · Referans ve borrowing · E0382 move hataları · E0499 ve E0502 · String ve &str farkı