Ten problem wydaje się sugerować, że to tylko szczegół implementacji (w memcpyporównaniu z ???), ale nie mogę znaleźć żadnego wyraźnego opisu różnic.
Ten problem wydaje się sugerować, że to tylko szczegół implementacji (w memcpyporównaniu z ???), ale nie mogę znaleźć żadnego wyraźnego opisu różnic.
Odpowiedzi:
Clonejest przeznaczony do dowolnych duplikatów: Cloneimplementacja typu Tmoż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.
CopyCecha reprezentuje wartości, które mogą być bezpiecznie powielane poprzez memcpy: rzeczy jak nadpisywaniem i przechodzącej argumentu przez wartość do funkcji są zawsze memcpys, a więc dla Copytypów, kompilator wie, że nie musi brać pod uwagę ruch .
Clonejest to głęboka kopia i czy Copyjest to kopia w tle?
Cloneotwiera 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ż 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.
Nawiasem mówiąc, każdy Copytyp 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ę.
yuzyskać przeniesienie x, a nie jego kopię, jak w przypadku ostatniego zakomentowanego przykładu w = v. Jak byś to określił?
Copyma być zaimplementowany dla „tanich” typów, jak u8w 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ę.
Copycechy 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 Copyvs, Cloneto 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.