En Rust, en raison du système strict de possession de la mémoire, un mécanisme de durée de vie (lifetimes) a été introduit, permettant au compilateur de vérifier la validité des références. Cependant, indiquer manuellement des annotations de durée de vie serait fastidieux, c'est pourquoi des règles d'élision ont été ajoutées au langage, permettant au compilateur de déduire automatiquement la durée de vie dans certains cas.
Sans une gestion appropriée des durées de vie des références, il est possible de rencontrer des erreurs de pointeurs suspendus (dangling pointers) ou de course de mémoire. Si le programmeur devait toujours spécifier explicitement les durées de vie, cela compliquerait fortement le développement.
Le compilateur Rust utilise les règles d'élision de durée de vie pour déterminer automatiquement, dans des signatures de fonctions courantes, quelles durées de vie doivent être liées entre les références d'entrée et les valeurs retournées. Cela réduit la quantité de code boilerplate et rend l'API plus compréhensible tout en maintenant la sécurité.
Exemple de code :
fn get_first(s: &str) -> &str { // élision de durée de vie &s[..1] }
Ici, le compilateur déduit la durée de vie du résultat — elle est égale à la durée de vie du paramètre d'entrée s.
Caractéristiques clés :
Pourquoi ne peut-on pas toujours omettre les durées de vie et compter sur les règles d'élision ?
L'élision ne fonctionne que dans des situations "simples". Par exemple, si une fonction retourne l'une des références d'entrée, le compilateur peut lier leurs durées de vie, mais quand il y a plusieurs relations non évidentes, une erreur de compilation se produit et il faut tout annoter explicitement.
fn pick<'a>(a: &'a str, b: &'a str, first: bool) -> &'a str { if first { a } else { b } } // Ici, il faut indiquer explicitement 'a, sinon le compilateur ne pourra pas comprendre la relation.
Peut-on omettre la durée de vie pour une structure si elle ne contient que des champs de référence ?
Non, si une structure contient des champs de référence, elle doit avoir un paramètre de durée de vie pour garantir que l'instance de la structure ne survit pas à ses données.
struct Foo<'a> { data: &'a str, }
Que se passe-t-il si l'on essaie de retourner une référence sur une variable locale ?
Le compilateur renverra une erreur, même si formellement les règles d'élision pourraient "déduire" la durée de vie. Rust suit la durée de vie non seulement par type, mais aussi par étendue de visibilité.
Un programmeur a écrit une API où il n'a pas spécifié explicitement la durée de vie, retournant une référence sur un tampon temporaire local à l'intérieur de la fonction. Le compilateur a rejeté le code, mais en essayant de "contourner" l'erreur, des annotations de durées de vie incorrectes ont été ajoutées, entraînant plusieurs erreurs déroutantes.
Avantages :
Inconvénients :
Dans l'API d'une bibliothèque, seules les annotations de durées de vie correctes sont utilisées là où cela est réellement nécessaire. Le reste est couvert par les règles automatiques d'élision, rendant le code concis et clair.
Avantages :
Inconvénients :