In Rust definiëren lifetimes het zichtbare bereik van referenties, zodat de compiler kan bevestigen dat de aanwijzers niet "oorzaken" (geen zwevende referenties) zijn. Dit biedt de garantie van geheugenveiligheid tijdens compilatie zonder de noodzaak van een garbage collector.
Wanneer je met referenties werkt, vereist Rust dat je de lifetime expliciet aangeeft wanneer de compiler deze niet kan afleiden. Dit gebeurt meestal met behulp van de syntaxis 'a:
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { if x.len() > y.len() { x } else { y } }
Hier hebben beide parameters en de retourwaarde dezelfde lifetime, wat garandeert dat de geretourneerde referentie niet langer leeft dan een van de argumenten.
Lifetimes veranderen de lifetime van data niet, ze beschrijven het alleen aan de compiler.
Is het mogelijk om een referentie naar een lokale variabele binnen een functie te retourneren?
Nee, dat kan niet: een dergelijke variabele wordt vernietigd bij het verlaten van de functie. Voorbeeld:
fn foo() -> &String { // compilatiefout! let s = String::from("hello"); &s } // referentie naar s wordt ongeldig
De compiler laat je niet zo’n code compileren: hij beschermt je tegen het gebruik van referenties naar vernietigde gegevens.
Verhaal
In het team waren er veel geheugenlekken toen functies per ongeluk referenties naar lokale buffers retourneerden. Dit werkte niet, en alleen de compiler die begon te klagen over lifetimes redde de situatie. Vanwege de frequente verschijning van dergelijke fouten werd de regel ingevoerd om de lifetime expliciet aan te geven als een functie met complexe structuren met geneste referenties werkte.
Verhaal
In een project was er generieke code geschreven voor het cachen van gegevens. Bij onjuiste ontwerp van de lifetime generieke parameters traden foutmeldingen op zoals "kan lifetime niet afleiden" en het werd onmogelijk om de lifetime van gegevens die in de cache werden opgeslagen af te leiden. Dit leidde tot het instellen van lifetime annotaties door middel van gokken en proberen, totdat er werd besloten om gecachede en niet-gecachede gegevens in verschillende structuren te scheiden.
Verhaal
Een van mijn collega's probeerde een verbindingpool te implementeren met gebruik van referenties naar verbindingsobjecten, maar hield er geen rekening mee dat de lifetimes van de verbindingen niet overeenkwamen met de lifetime van de pool. Uiteindelijk ontstonden er zwevende referenties na het vrijgeven van verbindingen, wat pas werd opgemerkt tijdens uitgebreide tests. Daarna stapte het project over op veilige wrappers (Arc<Mutex<T>>).