Ten problem wydaje się sugerować, że to tylko szczegół implementacji (w memcpy
porównaniu z ???), ale nie mogę znaleźć żadnego wyraźnego opisu różnic.
Ten problem wydaje się sugerować, że to tylko szczegół implementacji (w memcpy
porównaniu z ???), ale nie mogę znaleźć żadnego wyraźnego opisu różnic.
Odpowiedzi:
Clone
jest przeznaczony do dowolnych duplikatów: Clone
implementacja typu T
może wykonywać dowolnie skomplikowane operacje wymagane do utworzenia nowego T
. Jest to normalna cecha (inna niż bycie w preludium) i dlatego wymaga używania jak normalnej cechy, z wywołaniami metod itp.
Copy
Cecha reprezentuje wartości, które mogą być bezpiecznie powielane poprzez memcpy
: rzeczy jak nadpisywaniem i przechodzącej argumentu przez wartość do funkcji są zawsze memcpy
s, a więc dla Copy
typów, kompilator wie, że nie musi brać pod uwagę ruch .
Clone
jest to głęboka kopia i czy Copy
jest to kopia w tle?
Clone
otwiera możliwość, że typ może wykonać głęboką lub płytką kopię: „dowolnie skomplikowane”.
Główna różnica polega na tym, że klonowanie jest jawne. Notacja niejawna oznacza ruch dla typu innego niż Copy
typ.
// 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.
Nawiasem mówiąc, każdy Copy
typ też musi być Clone
. Jednak nie muszą robić tego samego! W przypadku własnych typów .clone()
może to być dowolna metoda, którą wybierzesz, podczas gdy niejawne kopiowanie zawsze wyzwoli a memcpy
, a nie clone(&self)
implementację.
y
uzyskać przeniesienie x
, a nie jego kopię, jak w przypadku ostatniego zakomentowanego przykładu w = v
. Jak byś to określił?
Copy
ma być zaimplementowany dla „tanich” typów, jak u8
w przykładzie. Jeśli piszesz dość ciężki typ, dla którego uważasz, że ruch jest bardziej efektywny niż kopia, nie sugeruj się tym Copy
. Zauważ, że w przypadku u8 nie można być bardziej wydajnym z ruchem, ponieważ pod maską prawdopodobnie wiązałoby się to przynajmniej z kopią wskaźnika - która jest już tak droga jak kopia u8, więc po co zawracać sobie głowę.
Copy
cechy ma wpływ na ukryte zakresy zmiennych w czasie życia? Jeśli tak, myślę, że jest to godne uwagi.
Jak już opisano w innych odpowiedziach:
Copy
jest niejawny, niedrogi i nie może być ponownie zaimplementowany (memcpy).Clone
jest jawna, może być kosztowna i może zostać dowolnie ponownie wdrożona.To, czego czasami brakuje w dyskusji o Copy
vs, Clone
to to, że wpływa on również na to, jak kompilator używa ruchów w porównaniu z automatycznymi kopiami. Na przykład:
#[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);
}
Pierwszy przykład ( PointCloneAndCopy
) działa tutaj dobrze ze względu na niejawną kopię, ale drugi przykład ( PointCloneOnly
) spowodowałby błąd przy użyciu po przeniesieniu:
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
Aby uniknąć niejawnego ruchu, możemy jawnie wywołać let p2 = p1.clone();
.
Może to rodzić pytanie, jak wymusić ruch typu, który implementuje cechę Copy? . Krótka odpowiedź: nie możesz / nie ma sensu.