Le borrowing (emprunt) en Rust est un mécanisme d'"emprunt" temporaire d'une variable via des références. Rust distingue l'emprunt immuable (&T) et l'emprunt mutable (&mut T). Il peut exister autant de références immuables que désiré, mais seulement une référence mutable. Il n'est pas possible d'avoir simultanément une référence mutable et des références immuables sur un même objet.
Cette règle garantit l'absence de data race à l'étape de compilation et rend Rust sûr pour la programmation concurrente.
Exemple :
let mut value = 5; let r1 = &value; let r2 = &value; // let r3 = &mut value; // ERREUR : on ne peut pas créer &mut tant qu'il y a & println!("{} {}", r1, r2); // r3 est interdit jusqu'à la fin de la portée de r1/r2
Question : Peut-on créer dans une même portée une référence &mut et autant de références & qu'on le souhaite sur un même objet ?
Réponse typiquement incorrecte : Oui, mais seulement si elles ne se chevauchent pas en termes de durée de vie.
Réponse correcte : Il ne peut pas exister simultanément une référence mutable et une référence immuable sur un même objet (même si les accès à celles-ci ne se chevauchent pas statiquement dans le code). Tant qu'une référence est active, les autres sont interdites. La sécurité est vérifiée par le borrow checker.
Exemple :
let mut x = 10; let y = &x; let z = &mut x; // Erreur, y est encore dans la portée println!("{}", y); // y est nécessaire plus tard
Histoire
Dans un grand projet de traitement de données parallèle, un développeur a décidé d'utiliser des références immuables sur un vecteur, puis a tenté d'en obtenir une mutable pour le tri. Le code fonctionnait lors des tests, mais a cessé de compiler après un refactoring, car la durée de vie des références immuables s'est étendue.
Histoire
Dans un service interne, la structure était modifiée via &mut tout en maintenant des références sur les champs pour un envoi ultérieur dans un autre thread. Une course de données et un crash sont survenus à cause du non-respect des règles de borrowing - Rust protège contre cela uniquement au moment de la compilation, mais les erreurs étaient dans des blocs unsafe, où les garanties sont supprimées.
Histoire
Documentation incorrecte de l'API : la bibliothèque acceptait simultanément & et &mut sur différents champs de la structure, mais en raison de l’aliasing, l'invariant était violé, entraînant des bogues difficiles à identifier avec les services intégrant cette bibliothèque.