Menu
Coddy logo textTech

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.

TraitComportementUtilisation
CopyImplicite, automatiqueDonnées simples stockées uniquement sur la pile
CloneExplicite 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é.

challenge icon

Défi

Facile

Construisons 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 publics x et y (tous deux de type i32). Comme elle ne contient que des types primitifs, dérive à la fois Copy et Clone afin qu’elle puisse être copiée implicitement lors d’une affectation.
    • NamedLocation : avec un champ public name (String) et des champs x/y (i32). Comme elle contient une String, tu peux uniquement dériver Clone, et non Copy. 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 GridPoint peut être affecté à une autre variable et que les deux restent valides (copie implicite)
    • Un NamedLocation doit utiliser .clone() pour créer un duplicata tout en conservant l’original valide

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})"
}
quiz iconTestez-vous

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

Entraînez-vous par vous-même : Compilateur Rust en ligne