Anmerkung des Herausgebers : Diese Frage wurde vor Rust 1.0 gestellt, und einige der Aussagen in der Frage sind in Rust 1.0 nicht unbedingt wahr. Einige Antworten wurden aktualisiert, um beide Versionen zu adressieren.
Ich habe diese Struktur
struct Triplet {
one: i32,
two: i32,
three: i32,
}
Wenn ich dies an eine Funktion übergebe, wird es implizit kopiert. Jetzt habe ich manchmal gelesen, dass einige Werte nicht kopierbar sind und daher verschoben werden müssen.
Wäre es möglich, diese Struktur Triplet
nicht kopierbar zu machen ? Wäre es beispielsweise möglich, ein Merkmal zu implementieren, das Triplet
nicht kopierbar und daher "beweglich" wäre?
Ich habe irgendwo gelesen, dass man das Clone
Merkmal implementieren muss, um Dinge zu kopieren, die nicht implizit kopierbar sind, aber ich habe nie darüber gelesen, dass etwas implizit kopierbar ist und es nicht kopierbar ist, sodass es sich stattdessen bewegt.
Macht das überhaupt Sinn?
Antworten:
Vorwort : Diese Antwort wurde geschrieben , bevor Opt-in - built-in Zügen -specifically die
Copy
Aspekte umgesetzt -were. Ich habe Blockzitate verwendet, um die Abschnitte anzugeben, die nur für das alte Schema galten (den, der galt, als die Frage gestellt wurde).Typen werden jetzt standardmäßig verschoben, dh wenn Sie einen neuen Typ definieren, wird dieser nur implementiert,
Copy
wenn Sie ihn explizit für Ihren Typ implementieren:Die Implementierung kann nur existieren, wenn jeder Typ im neuen enthalten ist
struct
oderenum
selbst istCopy
. Wenn nicht, gibt der Compiler eine Fehlermeldung aus. Es kann auch nur existieren , wenn der Typ nicht funktioniert eine hatDrop
Implementierung.Um die Frage zu beantworten, die Sie nicht gestellt haben ... "Was ist mit Zügen und Kopieren los?":
Zunächst definiere ich zwei verschiedene "Kopien":
(&usize, u64)
dies der Fall ist, sind es 16 Bytes auf einem 64-Bit-Computer, und eine flache Kopie würde diese 16 Bytes nehmen und ihre replizieren Wert in einem anderen 16-Byte-Speicherblock, ohne denusize
am anderen Ende des zu berühren&
. Das heißt, es ist gleichbedeutend mit einem Anrufmemcpy
.Rc<T>
nur die Referenzanzahl erhöht, und bei einer semantischen Kopie von a wirdVec<T>
eine neue Zuordnung erstellt und anschließend jedes gespeicherte Element semantisch vom alten in das neue kopiert. Dies können tiefe Kopien (z. B.Vec<T>
) oder flache Kopien sein (z. B.Rc<T>
berührt das Gespeicherte nichtT
). DiesClone
ist lose definiert als der kleinste Arbeitsaufwand, der erforderlich ist, um einen Wert vom TypT
innerhalb von a&T
nach semantisch zu kopierenT
.Rust ist wie C, jede Verwendung eines Werts nach Wert ist eine Bytekopie:
Sie sind Byte-Kopien, unabhängig davon, ob sie
T
verschoben werden oder "implizit kopierbar" sind. (Um klar zu sein, es handelt sich nicht unbedingt um buchstäblich byteweise Kopien zur Laufzeit: Der Compiler kann die Kopien optimieren, wenn das Verhalten des Codes erhalten bleibt.)Bei Byte-Kopien gibt es jedoch ein grundlegendes Problem: Sie haben doppelte Werte im Speicher, die sehr schlecht sein können, wenn sie Destruktoren haben, z
Wenn
w
nur eine einfache Bytekopie vonv
wäre, würden zwei Vektoren auf dieselbe Zuordnung zeigen, beide mit Destruktoren, die sie freigeben ... was eine doppelte Freigabe verursacht , was ein Problem ist. NB. Dies wäre vollkommen in Ordnung, wenn wir eine semantische Kopie vonv
in machen würdenw
, da dies dannw
seine eigene Unabhängigkeit wäreVec<u8>
und die Destruktoren sich nicht gegenseitig mit Füßen treten würden .Hier gibt es einige mögliche Korrekturen:
w
eine eigene Zuordnung hat, wie z. B. C ++ mit ihren Kopierkonstruktoren.v
nicht mehr verwendet werden können und der Destruktor nicht ausgeführt wird.Das Letzte ist, was Rust tut: Eine Verschiebung ist nur eine Verwendung nach Wert, bei der die Quelle statisch ungültig ist, sodass der Compiler die weitere Verwendung des jetzt ungültigen Speichers verhindert.
Typen mit Destruktoren müssen verschoben werden, wenn sie als Wert verwendet werden (auch bekannt als beim Kopieren von Bytes), da sie eine Ressource verwalten / besitzen (z. B. eine Speicherzuweisung oder ein Dateihandle) und es sehr unwahrscheinlich ist, dass eine Bytekopie diese korrekt dupliziert Eigentum.
"Nun ... was ist eine implizite Kopie?"
Stellen Sie sich einen primitiven Typ vor wie
u8
: Eine Byte-Kopie ist einfach, kopieren Sie einfach das einzelne Byte, und eine semantische Kopie ist genauso einfach, kopieren Sie das einzelne Byte. Insbesondere wird ein Byte - Kopie ist eine semantische Kopie ... Rust hat sogar ein Einbau-MerkmalCopy
, dass Aufnahmen , die Typen identisch semantische und Byte - Kopien haben.Daher sind
Copy
By-Value-Verwendungen für diese Typen auch automatisch semantische Kopien, sodass die Verwendung der Quelle absolut sicher ist.Wie oben erwähnt, sind integrierte Opt-In-Merkmale implementiert, sodass der Compiler kein automatisches Verhalten mehr aufweist. Die in der Vergangenheit für das automatische Verhalten verwendete Regel entspricht jedoch den Regeln für die Überprüfung, ob die Implementierung zulässig ist
Copy
.quelle
Am einfachsten ist es, etwas in Ihren Typ einzubetten, das nicht kopierbar ist.
Die Standardbibliothek bietet einen " Markertyp " für genau diesen Anwendungsfall: NoCopy . Beispielsweise:
quelle