Lorsque vous passez une variable à une fonction, elle peut être soit passée par référence (borrow, avec & ou &mut), soit déplacée (move, sans référence).
Borrow : une référence aux données est passée. Les données restent accessibles après l'appel de la fonction, mais pour une référence immuable, le contenu ne peut pas être modifié, et pour une référence mutable, il ne peut y avoir qu'une seule référence active.
fn read_length(s: &String) -> usize { s.len() }
Move : la variable est entièrement "déplacée" dans la fonction. Après le passage, vous ne pourrez plus utiliser la variable d'origine — elle a été déplacée, et toute tentative d'accès entraînera une erreur de compilation.
fn destroy(s: String) { println!("{}", s); } // s sera détruite à la sortie let s = String::from("world"); destroy(s); // s ne peut plus être utilisée
Cela prévient la double libération de mémoire et d'autres erreurs de propriété.
Puis-je utiliser une variable après qu'elle a été passée à une fonction par valeur (move) ?
Non ! Après avoir passé une variable par valeur — par exemple, String — la variable d'origine devient invalide :
let s = String::from("abc"); consume(s); // s n'est plus valide ici println!("{}", s); // erreur de compilation
Beaucoup de gens confondent cela avec le comportement des types avec Copy (par exemple, i32), où la variable reste valide après le passage.
Histoire
Un jeune programmeur a écrit une fonction de traitement de chaîne qui prenait String et non &String. En conséquence, la chaîne d'origine devenait inaccessible après l'appel de la fonction, ce qui entraînait un double chargement et une allocation supplémentaire de mémoire dans le serveur de journalisation.
Histoire
Dans un microservice, une ressource critique était passée à différents threads par move, après quoi les tentatives d'accès à cette ressource dans le thread principal entraînaient une erreur de compilation. Il a fallu refactoriser d'urgence l'architecture pour que la ressource soit passée par référence ou via des wrappers tels que Arc.
Histoire
Dans le parseur d'événements interne, lors du traitement des données, une variable était rapidement déplacée (move) dans un closure, après quoi les accès à celle-ci en dehors du closure entraînaient des erreurs de compilation. Le problème n'a été remarqué qu'au cours de la revue — un style obligatoire d'utilisation de borrow a été introduit pour les données qui devaient vivre plus longtemps que la fonction locale.