Clone et Copy
Fait partie de la section Object Oriented Programming du Journey Rust de Coddy — leçon 37 sur 61.
En Rust, lorsque vous assignez une valeur à une autre variable, la propriété (ownership) est généralement déplacée — la variable d'origine devient invalide. Cependant, deux traits standards modifient ce comportement : Clone et Copy.
Le trait Copy permet la copie bit à bit implicite. Lorsqu'un type implémente Copy, l'assigner à une autre variable crée une copie automatique au lieu de déplacer 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 via 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 flottants sont Copy, mais String ne l'est pas — il gère la mémoire du tas (heap). Si votre struct contient un String, vous ne pouvez dériver que Clone, et non Copy.
| Trait | Comportement | Utilisation |
|---|---|---|
Copy | Implicite, automatique | Données simples uniquement sur la pile |
Clone | Explicite via .clone() | Toutes données duplicables |
Pour les structures simples avec des champs primitifs, dériver les deux traits vous permet d'assigner librement des valeurs sans vous soucier de la possession (ownership).
Défi
FacileConstruisons un système de coordonnées qui démontre la différence entre les traits Copy et Clone ! Vous allez créer deux structs — l'une pouvant être copiée implicitement et l'autre nécessitant un clonage explicite — pour voir comment Rust gère la duplication différemment selon les traits que vous dérivez.
Vous organiserez votre code sur deux fichiers :
coordinates.rs: Définissez deux structs publiques qui représentent différents types de coordonnées :GridPoint— avec des champs publicsxety(tous deuxi32). Puisqu'elle ne contient que des types primitifs, dérivez à la foisCopyetCloneafin qu'elle puisse être copiée implicitement lors d'une affectation.NamedLocation— avec un champ publicname(String) et des champsx/y(i32). Puisqu'elle contient uneString, vous ne pouvez dériver queClone— pasCopy. Cette struct nécessitera des appels explicites à.clone()pour être dupliquée.
main.rs: Importez votre module coordinates et démontrez comment chaque struct se comporte différemment lors de la duplication. Créez des instances en utilisant les entrées fournies, puis montrez que :- Un
GridPointpeut être assigné à une autre variable et les deux restent valides (copie implicite) - Une
NamedLocationdoit utiliser.clone()pour créer un duplicata tout en gardant l'original valide
- Un
Votre sortie doit afficher à la fois les valeurs originales et les valeurs copiées/clonées pour 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)Vous recevrez trois entrées : la coordonnée x (à parser en i32), la coordonnée y (à parser en i32), et le nom de l'emplacement.
Aide-mémoire
Le trait Copy permet la copie bit à bit implicite. Lorsqu'un type implémente Copy, l'assigner à une autre variable crée une copie automatique au lieu de transférer la propriété (move) :
#[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 permet la copie profonde explicite via la méthode .clone() :
let p3 = p1.clone(); // Copie explicite
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 flottants sont Copy, mais String ne l'est pas. Si votre structure contient une String, vous ne pouvez dériver que Clone, pas Copy.
| Trait | Comportement | Utilisation |
|---|---|---|
Copy | Implicite, automatique | Données simples uniquement sur la pile (stack) |
Clone | Explicite via .clone() | Toute donnée duplicable |
Lors de la dérivation de Copy, vous devez également dériver Clone.
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 clone explicite (NamedLocation implémente uniquement Clone, pas Copy)
// Utiliser .clone() pour créer un doublon
// TODO: Afficher l'emplacement original et l'emplacement cloné
// 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 Object Oriented Programming
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édia