Introduction : Le moment où Rust ne vous « comprend pas »
\nVous écrivez en Rust. Tout va bien. Le compilateur se tait. Vous vous sentez comme le maître de la mémoire. Et puis — boum ! — elle apparaît :
\nerror[E0507]: cannot move out of borrowed content\n --> src/main.rs:10:9\n |\n10 | let value = self.data;\n | ^^^^^^^^^ move occurs because value has type `String`, which does not implement the `Copy` trait\n\nOu sa proche parente :
\nerror[E0507]: cannot move out of `*self` which is behind a shared reference\n\nSi vous êtes débutant, cela ressemble à une malédiction magique. Si vous êtes expérimenté, vous savez que ce n'est qu'une leçon de plus du Borrow Checker (vérificateur d'emprunts).
\nDans cet article, nous allons voir ce que signifient ces erreurs, pourquoi Rust est si strict, et — surtout — comment les corriger avec des exemples de code concrets. Ce n'est pas une théorie ennuyeuse, mais un guide pratique pour survivre dans le monde de la possession et de l'emprunt.
\n\nL'essentiel en bref : Possession, emprunt et « déplacement »
\nAvant de plonger dans les détails, rappelons les trois piliers de Rust :
\n- \n
- Possession (Ownership) : Chaque valeur en Rust a exactement un propriétaire. \n
- Emprunt (Borrowing) : Vous pouvez « emprunter » une valeur par référence (&T — immuable, &mut T — mutable), sans en prendre la possession. \n
- Déplacement (Move) : Lorsque vous assignez une valeur à une nouvelle variable ou la passez à une fonction sans &, la possession est déplacée. La variable d'origine devient invalide. \n
L'erreur cannot move out of borrowed content survient précisément lorsque vous essayez de déplacer une valeur sur laquelle vous n'avez qu'une référence (&T). Rust l'interdit car après le déplacement, la référence deviendrait « pendante » (dangling pointer). Et Rust, comme vous le savez, ne pardonne pas ce genre de choses.
Scénario 1 : Tentative de déplacer un champ de structure via &self
\nLe cas le plus fréquent. Vous avez une structure et vous voulez retourner l'un de ses champs depuis une méthode qui prend &self.
Code avec erreur
\nstruct User {\n name: String,\n age: u32,\n}\n\nimpl User {\n // On essaie de donner name "définitivement"\n fn give_name(&self) -> String {\n self.name // ERREUR !\n }\n}\n\nfn main() {\n let user = User { name: "Alice".to_string(), age: 30 };\n let name = user.give_name();\n println!("{}", name);\n}\n\nLe compilateur dira :
\nerror[E0507]: cannot move out of `self.name` which is behind a shared reference\n\nPourquoi ? self est un &User. Nous ne possédons pas self, nous l'avons seulement emprunté. Essayer de déplacer self.name (type String, qui n'implémente pas Copy) signifie qu'après l'appel de la méthode, le propriétaire (user) perdra son champ. Mais la référence &self existe toujours ! Rust nous protège d'une catastrophe.
Comment corriger ?
\nIl y a trois approches principales, selon votre besoin.
\n\nMéthode 1 : Clonage (la plus simple, mais pas toujours efficace)
\nSi vous avez besoin de la valeur elle-même, pas d'une référence, et que vous êtes prêt à payer pour la copie :
\nfn give_name(&self) -> String {\n self.name.clone() // Fonctionne ! On crée une copie.\n}\n\nInconvénient : clone() pour String fait une copie complète des données dans le tas. Pour les grandes chaînes, c'est coûteux.
Méthode 2 : Retourner une référence (modification de la signature)
\nSi le code appelant a besoin seulement d'une référence au champ :
\nfn give_name(&self) -> &String {\n &self.name // On retourne une référence, pas de déplacement\n}\n\nMaintenant, on ne déplace pas, on emprunte. Tout est correct. Le code appelant obtiendra un