Questo problema sembra implicare che sia solo un dettaglio di implementazione ( memcpy
rispetto a ???), ma non riesco a trovare alcuna descrizione esplicita delle differenze.
Questo problema sembra implicare che sia solo un dettaglio di implementazione ( memcpy
rispetto a ???), ma non riesco a trovare alcuna descrizione esplicita delle differenze.
Risposte:
Clone
è progettato per duplicazioni arbitrarie: Clone
un'implementazione per un tipo T
può eseguire operazioni arbitrariamente complicate necessarie per crearne uno nuovo T
. È un tratto normale (oltre a essere nel preludio), quindi richiede di essere utilizzato come un tratto normale, con chiamate di metodo, ecc.
Il Copy
tratto rappresenta valori che possono essere duplicati in modo sicuro tramite memcpy
: cose come riassegnazioni e passaggio di un argomento per valore a una funzione sono sempre memcpy
s, quindi per i Copy
tipi, il compilatore capisce che non è necessario considerarli una mossa .
Clone
una copia profonda e una copia Copy
replicata?
Clone
apre la possibilità che il tipo possa fare una copia profonda o superficiale: "arbitrariamente complicata".
La differenza principale è che la clonazione è esplicita. La notazione implicita significa mossa per un non- Copy
tipo.
// 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.
A proposito, anche ogni Copy
tipo deve essere Clone
. Tuttavia, non sono tenuti a fare la stessa cosa! Per i tuoi tipi, .clone()
può essere un metodo arbitrario di tua scelta, mentre la copia implicita attiverà sempre a memcpy
, non l' clone(&self)
implementazione.
y
ottenere uno spostamento x
, non una copia, come nel tuo ultimo esempio commentato w = v
. Come lo specificheresti?
Copy
è pensato per essere implementato per tipi "economici", come u8
nell'esempio. Se scrivi un tipo piuttosto pesante, per il quale pensi che una mossa sia più efficiente di una copia, non farlo impl Copy
. Nota che nel caso di u8, non puoi essere più efficiente con una mossa, poiché sotto il cofano probabilmente comporterebbe almeno una copia del puntatore - che è già costosa come una copia di u8, quindi perché preoccuparsi.
Copy
tratto ha un impatto sugli ambiti di vita impliciti delle variabili? Se è così, penso che sia degno di nota.
Come già coperto da altre risposte:
Copy
è implicito, poco costoso e non può essere reimplementato (memcpy).Clone
è esplicito, può essere costoso e può essere reimplementato arbitrariamente.Ciò che a volte manca nella discussione di Copy
vs Clone
è che influisce anche sul modo in cui il compilatore utilizza le mosse rispetto alle copie automatiche. Per esempio:
#[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);
}
Il primo example ( PointCloneAndCopy
) funziona bene qui a causa della copia implicita, ma il secondo example ( PointCloneOnly
) avrebbe un errore con un utilizzo dopo lo spostamento:
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
Per evitare la mossa implicita, potremmo chiamare esplicitamente let p2 = p1.clone();
.
Ciò potrebbe sollevare la questione di come forzare una mossa di un tipo che implementa il tratto Copia? . Risposta breve: non puoi / non ha senso.