Was ist der Unterschied zwischen Kopieren und Klonen?

127

Dieses Problem scheint zu implizieren, dass es sich nur um ein Implementierungsdetail handelt ( memcpyvs ???), aber ich kann keine explizite Beschreibung der Unterschiede finden.

user12341234
quelle
Rusts Quellcode hat relevante Erklärungen
duan

Antworten:

114

Cloneist für beliebige Duplikate ausgelegt: Eine CloneImplementierung für einen Typ Tkann beliebig komplizierte Operationen ausführen, die zum Erstellen eines neuen erforderlich sind T. Es ist ein normales Merkmal (außer im Vorspiel) und erfordert daher die Verwendung wie ein normales Merkmal mit Methodenaufrufen usw.

Das CopyMerkmal stellt Werte dar, die sicher dupliziert werden können über memcpy: Dinge wie Neuzuweisungen und das Übergeben eines Argument-By-Value an eine Funktion sind immer memcpys, und daher Copyversteht der Compiler für Typen, dass er diese nicht als Verschiebung betrachten muss .

huon
quelle
5
Kann ich verstehen, dass Clonees sich um eine Tiefenkopie und Copyeine Schattenkopie handelt?
Djvu
11
Cloneeröffnet die Möglichkeit, dass der Typ entweder eine tiefe oder eine flache Kopie erstellt: "willkürlich kompliziert".
Poolie
85

Der Hauptunterschied besteht darin, dass das Klonen explizit ist. Implizite Notation bedeutet Verschieben für einen Nicht- CopyTyp.

// u8 implements Copy
let x: u8 = 123;
let y = x;
// x can still be used
println!("x={}, y={}", x, y);

// Vec<u8> implements Clone, but not Copy
let v: Vec<u8> = vec![1, 2, 3];
let w = v.clone();
//let w = v // This would *move* the value, rendering v unusable.

Übrigens muss auch jeder CopyTyp sein Clone. Sie müssen jedoch nicht dasselbe tun! .clone()Kann für Ihre eigenen Typen eine beliebige Methode Ihrer Wahl sein, während implizites Kopieren immer eine memcpyund nicht die clone(&self)Implementierung auslöst .

mdup
quelle
1
Cool! Dies klärt eine sekundäre Frage, die ich hatte, ob das Clone-Merkmal implizites Kopieren ermöglicht. Es stellt sich heraus, dass diese Frage und diese mehr verwandt waren als ich dachte. Vielen Dank!
user12341234
Angenommen, Sie möchten in Ihrem ersten Beispiel yeinen Umzug erhalten x, keine Kopie davon, wie in Ihrem letzten auskommentierten Beispiel w = v. Wie würden Sie das spezifizieren?
Johnbakers
2
Sie können nicht und Sie nicht, weil Copyes für "billige" Typen implementiert werden soll, wie u8im Beispiel. Wenn Sie einen ziemlich schweren Typ schreiben, für den Sie glauben, dass ein Zug effizienter ist als eine Kopie, lassen Sie ihn nicht implizieren Copy. Beachten Sie, dass Sie im U8-Fall mit einem Umzug möglicherweise nicht effizienter sein können, da unter der Haube wahrscheinlich zumindest eine Zeigerkopie erforderlich ist - die bereits so teuer ist wie eine U8-Kopie. Warum also die Mühe machen?
Mdup
Bedeutet dies, dass das Vorhandensein des CopyMerkmals einen Einfluss auf den impliziten Lebensbereich von Variablen hat? Wenn ja, finde ich das bemerkenswert.
Brian Cain
6

Wie bereits von anderen Antworten abgedeckt:

  • Copy ist implizit, kostengünstig und kann nicht erneut implementiert werden (memcpy).
  • Clone ist explizit, kann teuer sein und kann willkürlich neu implementiert werden.

Was manchmal in der Diskussion von Copyvs fehlt, Cloneist, dass es auch beeinflusst, wie der Compiler Moves vs automatische Kopien verwendet. Zum Beispiel:

#[derive(Debug, Clone, Copy)]
pub struct PointCloneAndCopy {
    pub x: f64,
}

#[derive(Debug, Clone)]
pub struct PointCloneOnly {
    pub x: f64,
}

fn test_copy_and_clone() {
    let p1 = PointCloneAndCopy { x: 0. };
    let p2 = p1; // because type has `Copy`, it gets copied automatically.
    println!("{:?} {:?}", p1, p2);
}

fn test_clone_only() {
    let p1 = PointCloneOnly { x: 0. };
    let p2 = p1; // because type has no `Copy`, this is a move instead.
    println!("{:?} {:?}", p1, p2);
}

Das erste Beispiel ( PointCloneAndCopy) funktioniert hier aufgrund der impliziten Kopie einwandfrei, aber das zweite Beispiel ( PointCloneOnly) würde bei einer Verwendung nach dem Verschieben einen Fehler verursachen:

error[E0382]: borrow of moved value: `p1`
  --> src/lib.rs:20:27
   |
18 |     let p1 = PointCloneOnly { x: 0. };
   |         -- move occurs because `p1` has type `PointCloneOnly`, which does not implement the `Copy` trait
19 |     let p2 = p1;
   |              -- value moved here
20 |     println!("{:?} {:?}", p1, p2);
   |                           ^^ value borrowed here after move

Um die implizite Verschiebung zu vermeiden, könnten wir explizit aufrufen let p2 = p1.clone();.

Dies kann die Frage aufwerfen, wie eine Bewegung eines Typs erzwungen werden kann, der das Merkmal Kopieren implementiert. . Kurze Antwort: Sie können / machen keinen Sinn.

bluenote10
quelle
@Shepmaster Ich habe es entfernt, obwohl ich es viel besser lesbar finde, da es die schöne Farbcodierung des Rust-Compilers enthält und ich speziell darauf geachtet habe, dass alle suchrelevanten Wörter auch im Text enthalten sind.
bluenote10