In Rust is er, vanwege het strikte geheugenbeheersysteem, een mechanisme voor levensduur (lifetimes) ontstaan, waarmee de compilator de geldigheid van verwijzingen kan controleren. Het handmatig opgeven van levensduurannotaties zou echter vermoeiend zijn, daarom zijn er regels voor "elision" in de taal geïntroduceerd, waarmee de compilator in bepaalde gevallen de levensduur automatisch kan afleiden.
Zonder correct beheer van de levensduur van verwijzingen kunnen er fouten optreden zoals "dangling pointers" of race conditions. Als de programmeur altijd expliciet levensduur zou moeten opgeven, zou dat de ontwikkeling aanzienlijk bemoeilijken.
De Rust-compiler gebruikt de regels voor levensduurvervangen om automatisch te bepalen welke levensduren moeten worden gekoppeld aan de invoerverwijzingen en de geretourneerde waarden in veelvoorkomende functietekens. Dit vermindert de hoeveelheid boilerplate-code en maakt de API begrijpelijker, terwijl de veiligheid behouden blijft.
Voorbeeldcode:
fn get_first(s: &str) -> &str { // levensduurvervangen &s[..1] }
Hier leidt de compiler de levensduur van de uitkomst af — deze is gelijk aan de levensduur van de invoerparameter s.
Belangrijkste kenmerken:
Waarom kunnen we levensduur niet altijd weglaten en ons baseren op de elisietregels?
Elision werkt alleen in "eenvoudige" situaties. Bijvoorbeeld, als een functie een van de invoerverwijzingen retourneert, kan de compiler hun levensduren koppelen, maar als er meerdere onduidelijke koppelingen zijn — ontstaat er een compilatiefout en moeten alle annotaties expliciet worden opgegeven.
fn pick<'a>(a: &'a str, b: &'a str, first: bool) -> &'a str { if first { a } else { b } } // Hier is het noodzakelijk om 'a expliciet op te geven, anders kan de compiler de relatie niet begrijpen.
Kan de levensduur worden weggelaten in een structuur als deze alleen verwijzingsvelden bevat?
Nee, als een structuur verwijzingsvelden bevat, moet deze een levensduurparameter hebben om te garanderen dat een instantie van de structuur niet langer leeft dan zijn gegevens.
struct Foo<'a> { data: &'a str, }
Wat gebeurt er als je probeert een verwijzing naar een lokale variabele te retourneren?
De compiler zal een foutmelding geven, zelfs als de elisietregels formeel de levensduur zouden kunnen "afleiden". Rust houdt levensduren bij op basis van type, maar ook op basis van scope.
Een programmeur schreef een API waarin de levensduur niet expliciet was vastgelegd en die een verwijzing naar een lokale tijdelijke buffer binnen een functie teruggaf. De compiler wees de code af, maar bij het proberen "om te gaan" met de fout werden onjuiste levensduurannotaties toegevoegd, waardoor verschillende verwarrende fouten ontstonden.
Voordelen:
Nadelen:
In de API van de bibliotheek worden correcte levensduurannotaties alleen daar gebruikt waar dat echt nodig is. Alles daarbuiten is gedekt door automatische elisietregels, wat de code beknopt en begrijpelijk maakt.
Voordelen:
Nadelen: