#8 Durées de vie
'a, annotations de lifetime
Pourquoi les lifetimes ?
Les lifetimes (durées de vie) sont le mécanisme par lequel Rust s'assure que les références sont toujours valides. Le compilateur vérifie que les données référencées vivent au moins aussi longtemps que la référence elle-même.
La plupart du temps, les lifetimes sont inférées automatiquement. Mais parfois, le compilateur a besoin d'annotations explicites pour comprendre les relations entre les références.
// Ce code ne compile PAS :
fn main() {
let r;
{
let x = 5;
r = &x; // x sera libéré à la fin de ce bloc
}
// println!("{r}"); // Erreur : x n'existe plus !
}Le compilateur refuse ce code car r référencerait une valeur qui n'existe plus. C'est exactement ce que les lifetimes empêchent.
Annotations de lifetime
Quand une fonction retourne une référence, le compilateur doit savoir à quelle donnée d'entrée elle est liée. Les annotations de lifetime, notées avec une apostrophe ('a), expriment cette relation.
fn le_plus_long<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() {
x
} else {
y
}
}
fn main() {
let s1 = String::from("bonjour le monde");
let resultat;
{
let s2 = String::from("salut");
resultat = le_plus_long(&s1, &s2);
println!("Le plus long : {resultat}");
}
}'a est un paramètre de lifetime. Il indique que la référence retournée vivra aussi longtemps que la plus courte des durées de vie de x et y.
Les annotations ne changent pas les durées de vie — elles les décrivent pour que le compilateur puisse vérifier la validité des références.
Élision de lifetime
Le compilateur Rust applique des règles d'élision qui permettent d'omettre les annotations dans les cas courants. Voici les trois règles :
- Chaque paramètre de référence reçoit son propre lifetime.
- S'il n'y a qu'un seul paramètre de référence, son lifetime est assigné à toutes les références en sortie.
- Si un des paramètres est
&selfou&mut self, le lifetime deselfest assigné aux références en sortie.
// Ces deux signatures sont équivalentes :
fn premier_mot(s: &str) -> &str { /* ... */ }
fn premier_mot<'a>(s: &'a str) -> &'a str { /* ... */ }
// Le compilateur applique automatiquement la règle 2Grâce à l'élision, vous n'aurez besoin d'annotations explicites que dans les cas où il y a ambiguïté — typiquement quand la fonction prend plusieurs références en paramètres.
Lifetimes dans les structs
Si une struct contient des références, elle doit déclarer un paramètre de lifetime. Cela garantit que les données référencées vivent au moins aussi longtemps que la struct.
struct Extrait<'a> {
texte: &'a str,
}
impl<'a> Extrait<'a> {
fn nouveau(source: &'a str, debut: usize, fin: usize) -> Self {
Extrait {
texte: &source[debut..fin],
}
}
}
fn main() {
let source = String::from("Rust est un langage sûr");
let extrait = Extrait::nouveau(&source, 5, 19);
println!("Extrait : \"{}\" vient de \"{source}\"", extrait.texte);
}Le paramètre 'a sur la struct Extrait signifie : "une instance d'Extrait ne peut pas vivre plus longtemps que la référence qu'elle contient dans son champ texte".
À vous de jouer
Essayez les commandes ci-dessous pour expérimenter avec les lifetimes :