Clone et Copy
Fait partie de la section Programmation Orientée Objet du Journey Rust de Coddy. Leçon 37 sur 61.
En Rust, lorsque vous affectez une valeur à une autre variable, la propriété est généralement transférée : la variable d’origine devient invalide. Cependant, deux traits standard modifient ce comportement : Clone et Copy.
Le trait Copy permet la copie implicite au niveau des bits. Lorsqu’un type implémente Copy, le fait de l’affecter à une autre variable crée une copie automatique au lieu de transférer la propriété :
#[derive(Copy, Clone)]
struct Point {
x: i32,
y: i32,
}
let p1 = Point { x: 10, y: 20 };
let p2 = p1; // p1 est copié, pas déplacé
println!("{}, {}", p1.x, p2.x); // Les deux sont valides !
Le trait Clone fournit une copie profonde explicite grâce à la méthode .clone(). Il est requis chaque fois que vous dérivez Copy, mais peut également être utilisé seul pour les types qui nécessitent une copie explicite :
let p3 = p1.clone(); // Copie explicite
Il existe une restriction importante : Copy ne peut être dérivé que pour les types dont tous les champs implémentent également Copy. Les types simples comme les entiers et les nombres flottants sont Copy, mais String ne l’est pas. Il gère la mémoire du tas. Si votre structure contient un String, vous pouvez uniquement dériver Clone, et non Copy.
| Trait | Comportement | Utilisation |
|---|---|---|
Copy | Implicite, automatique | Données simples stockées uniquement sur la pile |
Clone | Explicite via .clone() | Toutes les données duplicables |
Pour les structures simples avec des champs primitifs, dériver les deux traits vous permet d’attribuer librement des valeurs sans vous soucier de la propriété.
Défi
FacileConstruisons un système de coordonnées qui démontre la différence entre les traits Copy et Clone ! Tu vas créer deux structures, l’une qui peut être copiée implicitement et l’autre qui nécessite un clonage explicite, afin de voir comment Rust gère la duplication différemment selon les traits que tu dérives.
Tu organiseras ton code sur deux fichiers :
coordinates.rs: définis deux structures publiques qui représentent différents types de coordonnées :GridPoint: avec des champs publicsxety(tous deux de typei32). Comme elle ne contient que des types primitifs, dérive à la foisCopyetCloneafin qu’elle puisse être copiée implicitement lors d’une affectation.NamedLocation: avec un champ publicname(String) et des champsx/y(i32). Comme elle contient uneString, tu peux uniquement dériverClone, et nonCopy. Cette structure nécessitera des appels explicites à.clone()pour être dupliquée.
main.rs: importe ton module de coordonnées et montre comment chaque structure se comporte différemment lorsqu’elle est dupliquée. Crée des instances à l’aide des entrées fournies, puis montre que :- Un
GridPointpeut être affecté à une autre variable et que les deux restent valides (copie implicite) - Un
NamedLocationdoit utiliser.clone()pour créer un duplicata tout en conservant l’original valide
- Un
Ta sortie doit afficher les valeurs originales et copiées/clonées afin de prouver que les deux variables sont valides après la duplication :
Original point: ({x}, {y})
Copied point: ({x}, {y})
Original location: {name} at ({x}, {y})
Cloned location: {name} at ({x}, {y})Par exemple, avec les entrées 5, 10 et Home :
Original point: (5, 10)
Copied point: (5, 10)
Original location: Home at (5, 10)
Cloned location: Home at (5, 10)Tu recevras trois entrées : la coordonnée x (à analyser comme un i32), la coordonnée y (à analyser comme un i32) et le nom du lieu.
Essayez vous-même
mod coordinates;
use coordinates::{GridPoint, NamedLocation};
fn main() {
// Lire les entrées
let mut input1 = String::new();
std::io::stdin().read_line(&mut input1).expect("Failed to read line");
let x: i32 = input1.trim().parse().expect("Invalid number");
let mut input2 = String::new();
std::io::stdin().read_line(&mut input2).expect("Failed to read line");
let y: i32 = input2.trim().parse().expect("Invalid number");
let mut input3 = String::new();
std::io::stdin().read_line(&mut input3).expect("Failed to read line");
let name = input3.trim().to_string();
// TODO: Créer une instance de GridPoint avec x et y
// TODO: Démontrer la copie implicite en assignant à une autre variable
// (GridPoint implémente Copy, donc cela crée une copie automatiquement)
// TODO: Afficher le point original et le point copié
// Format: "Original point: ({x}, {y})"
// Format : "Copied point: ({x}, {y})"
// TODO: Créer une instance de NamedLocation avec name, x et y
// TODO: Démontrer le clonage explicite (NamedLocation n'implémente que Clone, pas Copy)
// Utiliser .clone() pour créer un doublon
// TODO: Print original and cloned location
// Format: "Original location: {name} at ({x}, {y})"
// Format: "Cloned location: {name} at ({x}, {y})"
}
Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.
Toutes les leçons de Programmation Orientée Objet
1Méthodes et comportement
Introduction aux blocs d'implémentationLe paramètre SelfMéthodes mutablesFonctions associéesBlocs d'implémentation multiplesChaînage de méthodesRécapitulatif - Actions sur le rectangle4Projet : Animal virtuel
Définir l'animalNourrir l'animal7Traits standards
Le trait DebugLe trait DisplayClone et CopyLes traits d'égalitéRécapitulatif - Point affichable10Projet : Système de documents
Le trait DrawComposant texte2Encapsulation et modules
Bases des modulesLe mot-clé publicChamps privésGettersSettersRécapitulatif - Casier sécurisé5Généricité
Structures génériquesMéthodes génériquesPlusieurs types génériquesFonctions génériquesRécapitulatif - Point de coordonnées8Les Traits comme contraintes
Syntaxe des contraintes de traitContraintes multiplesLa clause whereRetourner des types avec des traitsRécapitulatif - Afficheur générique11Design Patterns en Rust
Le pattern NewtypeCompositionLe trait DropFrom et IntoRécapitulatif - Mock de Smart Pointer3Enums avancées
Enums avec donnéesMéthodes sur les EnumsMatching des variantes de donnéesL'Enum Option revisitéeRécapitulatif - Enum Shape6Définition des Traits
Qu'est-ce qu'un Trait ?Implémenter des TraitsImplémentations par défautRedéfinir les comportements par défautTraits avec paramètresRécapitulatif - Lecteur multimédiaEntraînez-vous par vous-même : Compilateur Rust en ligne