Menu
Coddy logo textTech

Clone und Copy

Teil des Abschnitts Objektorientierte Programmierung der Rust-Journey von Coddy. Lektion 37 von 61.

In Rust wird beim Zuweisen eines Werts zu einer anderen Variable der Besitz typischerweise verschoben: Die ursprüngliche Variable wird ungültig. Zwei standardmäßige Traits ändern jedoch dieses Verhalten: Clone und Copy.

Das Copy-Trait ermöglicht implizites bitweises Kopieren. Wenn ein Typ Copy implementiert, erstellt die Zuweisung an eine andere Variable automatisch eine Kopie, anstatt den Besitz zu übertragen:

#[derive(Copy, Clone)]
struct Point {
    x: i32,
    y: i32,
}

let p1 = Point { x: 10, y: 20 };
let p2 = p1;  // p1 wird kopiert, nicht verschoben

println!("{}, {}", p1.x, p2.x);  // Beide sind gültig!

Das Clone-Trait ermöglicht explizites tiefes Kopieren über die Methode .clone(). Es ist immer erforderlich, wenn du Copy ableitest, kann aber auch allein für Typen verwendet werden, die explizites Kopieren benötigen:

let p3 = p1.clone();  // Explizite Kopie

Es gibt eine wichtige Einschränkung: Copy kann nur für Typen abgeleitet werden, bei denen alle Felder ebenfalls Copy implementieren. Einfache Typen wie Ganzzahlen und Gleitkommazahlen sind Copy, aber String ist es nicht. String verwaltet Heap-Speicher. Wenn deine Struktur einen String enthält, kannst du nur Clone und nicht Copy ableiten.

TraitVerhaltenVerwendung
CopyImplizit, automatischEinfache, ausschließlich auf dem Stack gespeicherte Daten
CloneExplizit über .clone()Beliebige duplizierbare Daten

Bei einfachen Strukturen mit primitiven Feldern kannst du durch das Ableiten beider Traits Werte frei zuweisen, ohne dir Gedanken über den Besitz machen zu müssen.

challenge icon

Aufgabe

Einfach

Erstelle ein Koordinatensystem, das den Unterschied zwischen den Traits Copy und Clone demonstriert! Du erstellst zwei Structs: eines, das implizit kopiert werden kann, und eines, das explizites Klonen erfordert. So kannst du sehen, wie Rust die Duplizierung je nach abgeleiteten Traits unterschiedlich behandelt.

Du organisierst deinen Code auf zwei Dateien:

  • coordinates.rs: Definiere zwei öffentliche Structs, die verschiedene Arten von Koordinaten darstellen:
    • GridPoint: mit öffentlichen Feldern x und y (beide i32). Da es nur primitive Typen enthält, leitest du sowohl Copy als auch Clone ab, damit es bei einer Zuweisung implizit kopiert werden kann.
    • NamedLocation: mit dem öffentlichen Feld name (String) und den Feldern x/y (i32). Da es einen String enthält, kannst du nur Clone, nicht aber Copy ableiten. Dieses Struct erfordert explizite Aufrufe von .clone(), um es zu duplizieren.
  • main.rs: Binde dein Koordinatenmodul ein und zeige, wie sich jedes Struct bei der Duplizierung anders verhält. Erstelle Instanzen mithilfe der bereitgestellten Eingaben und zeige anschließend:
    • Ein GridPoint kann einer anderen Variable zugewiesen werden, und beide bleiben gültig (implizite Kopie).
    • Ein NamedLocation muss .clone() verwenden, um ein Duplikat zu erstellen und gleichzeitig das Original gültig zu halten.

Deine Ausgabe sollte sowohl die ursprünglichen als auch die kopierten/geklonten Werte anzeigen, um zu belegen, dass beide Variablen nach der Duplizierung gültig sind:

Original point: ({x}, {y})
Copied point: ({x}, {y})
Original location: {name} at ({x}, {y})
Cloned location: {name} at ({x}, {y})

Beispielsweise mit den Eingaben 5, 10 und Home:

Original point: (5, 10)
Copied point: (5, 10)
Original location: Home at (5, 10)
Cloned location: Home at (5, 10)

Du erhältst drei Eingaben: die x-Koordinate (als i32 parsen), die y-Koordinate (als i32 parsen) und den Namen des Ortes.

Probier es selbst

mod coordinates;

use coordinates::{GridPoint, NamedLocation};

fn main() {
    // Eingaben lesen
    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: Erstelle eine GridPoint-Instanz mit x und y

    // TODO: Demonstriere implizites Kopieren durch Zuweisung an eine andere Variable
    // (GridPoint implementiert Copy, daher wird automatisch eine Kopie erstellt)

    // TODO: Gib den originalen und den kopierten Punkt aus
    // Format: "Original point: ({x}, {y})"
    // Format: "Copied point: ({x}, {y})"

    // TODO: Erstelle eine NamedLocation-Instanz mit name, x und y

    // TODO: Demonstriere explizites Klonen (NamedLocation implementiert nur Clone, nicht Copy)
    // Verwende .clone(), um ein Duplikat zu erstellen

    // TODO: Print original and cloned location
    // Format: "Original location: {name} at ({x}, {y})"
    // Format: "Cloned location: {name} at ({x}, {y})"
}
quiz iconTeste dich selbst

Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.

Alle Lektionen in Objektorientierte Programmierung

Übe selbstständig: Online-Rust-Compiler