Einleitung: Warum Copy und Clone nicht dasselbe sind?
\n\nJeder, der anfängt, Rust zu lernen, stößt früher oder später auf zwei rätselhafte Traits: Copy und Clone. Auf den ersten Blick tun sie dasselbe – sie ermöglichen es, ein Duplikat eines Wertes zu erhalten. Aber der Teufel steckt, wie immer, im Detail.
Stellen Sie sich vor, Sie schreiben ein Programm und übergeben einen Wert an eine Funktion. In C++ wäre das eine Kopie (oder eine Verschiebung, wenn Sie Glück haben). In Python – einfach eine weitere Referenz auf das Objekt. Aber in Rust... Rust zwingt Sie, explizit zu entscheiden: "Ich möchte, dass dieser Wert kopiert wird" oder "Ich möchte ihn unter Berücksichtigung der Kosten klonen".
\n\nGenau hier verläuft die Grenze zwischen Copy und Clone. Ersteres ist "einfaches Bit-für-Bit-Kopieren". Zweiteres ist "bewusstes, möglicherweise teures Duplizieren".
Lassen Sie uns anhand von Beispielen verstehen, wann und was zu verwenden ist, damit Ihr Rust-Code nicht nur korrekt, sondern auch effizient ist.
\n\nDas Wichtigste in Kürze: Copy vs Clone
\n\nWas ist Clone?
\n\nClone ist ein Trait, der Ihnen die Methode .clone() zur Verfügung stellt. Wenn Sie sie aufrufen, sagen Sie dem Compiler explizit: "Ich weiß, dass dies teuer sein kann, aber ich brauche eine unabhängige Kopie."
Eigenschaften von Clone:
- \n
- Kann für jeden Typ implementiert werden \n
- Erfordert einen expliziten Aufruf von
.clone()\n - Kann tiefe Kopien durchführen (z. B. für
VecoderString) \n - Ändert nicht die Eigentumssemantik – nach dem Klonen haben Sie zwei unabhängige Werte \n
Was ist Copy?
\n\nCopy ist ein "Marker"-Trait (ohne Methoden). Wenn ein Typ Copy implementiert, kann der Compiler seine Bits bei der Zuweisung oder Übergabe an eine Funktion einfach kopieren. Dies erfordert keinen Aufruf von .clone().
Eigenschaften von Copy:
- \n
- Funktioniert nur für einfache Typen, die sicher bitweise kopiert werden können \n
- Erfordert keinen expliziten Aufruf – das Kopieren erfolgt automatisch \n
- Der Typ muss trotzdem
Cloneimplementieren (es ist ein Supertrait) \n - Der ursprüngliche Wert bleibt nach dem Kopieren gültig \n
Praktische Beispiele: Wie es im Code funktioniert
\n\nBeispiel 1: Basistypen (Copy)
\n\nGanze Zahlen, Gleitkommazahlen, boolesche Werte – alle implementieren Copy:
fn main() {\n let x = 42;\n let y = x; // x wird nach y kopiert, x ist weiterhin verfügbar\n \n println!("x = {}, y = {}", x, y); // Funktioniert!\n}\n\nHier ist x ein i32, der Copy implementiert. Daher wird bei der Zuweisung y = x kopiert, und x bleibt gültig.
Beispiel 2: String (nur Clone)
\n\nString hingegen ist ein komplexer Typ, der Daten auf dem Heap speichert. Er implementiert Clone, aber nicht Copy:
fn main() {\n let s1 = String::from("Hallo, Rust!");\n let s2 = s1; // s1 wird nach s2 verschoben\n \n // println!("{}", s1); // Fehler! s1 besitzt die Daten nicht mehr\n \n let s3 = s2.clone(); // So geht es – explizites Klonen\n println!("s2 = {}, s3 = {}", s2, s3); // Funktioniert!\n}\n\nWarum kann String nicht Copy sein? Weil das bitweise Kopieren eines String zu zwei Zeigern auf denselben Heap-Speicher führen würde. Beim Drop beider Werte – doppelte Speicherfreigabe. Clone hingegen erstellt eine vollwertige, unabhängige Kopie.
Beispiel 3: Eigener Typ mit Copy
\n\nSie können Ihren eigenen Typ Copy machen, wenn er nur aus Copy-Typen besteht:
#[derive(Debug, Copy, Clone)]\nstruct Point {\n x: f64,\n y: f64,\n}\n\nfn main() {\n let p1 = Point { x: 1.0, y: 2.0 };\n let p2 = p1; // Wird kopiert, p1 bleibt erhalten\n \n println!