Apa perbedaan antara Copy dan Clone?

128

Masalah ini sepertinya menyiratkan bahwa ini hanya detail implementasi ( memcpyvs ???), tetapi saya tidak dapat menemukan deskripsi eksplisit tentang perbedaannya.

pengguna12341234
sumber
Kode sumber Rust memiliki penjelasan yang relevan
duan

Jawaban:

115

Clonedirancang untuk duplikasi sewenang-wenang: Cloneimplementasi untuk suatu jenis Tdapat melakukan operasi rumit sewenang-wenang yang diperlukan untuk membuat yang baru T. Ini adalah sifat normal (selain di awal), dan karenanya membutuhkan penggunaan seperti sifat normal, dengan pemanggilan metode, dll.

The Copysifat mewakili nilai-nilai yang dapat dengan aman digandakan melalui memcpy: hal-hal seperti penugasan kembali dan melewati sebuah argumen dengan-nilai ke fungsi selalu memcpys, dan sebagainya untuk Copyjenis, compiler mengerti bahwa hal itu tidak perlu mempertimbangkan mereka bergerak .

huon
sumber
5
Dapatkah saya memahami sebagai Clonesalinan dalam, dan Copysalinan bayangan?
Djvu
11
Clonemembuka kemungkinan bahwa tipe dapat melakukan salinan dalam atau dangkal: "rumit secara sewenang-wenang".
Poolie
85

Perbedaan utamanya adalah kloning itu eksplisit. Notasi implisit berarti pindah untuk non- Copytipe.

// 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.

Omong-omong, setiap Copytipe juga harus ada Clone. Namun, mereka tidak diharuskan melakukan hal yang sama! Untuk tipe Anda sendiri, .clone()bisa menjadi metode arbitrer pilihan Anda, sedangkan penyalinan implisit akan selalu memicu a memcpy, bukan clone(&self)implementasi.

mdup
sumber
1
Keren! Ini menjelaskan pertanyaan sekunder yang saya miliki mengenai apakah sifat Klon menyediakan penyalinan implisit. Ternyata pertanyaan itu dan yang ini lebih terkait dari yang saya kira. Terima kasih!
pengguna12341234
Dalam contoh pertama Anda, misalkan Anda ingin ydipindahkan x, bukan salinannya, seperti contoh terakhir yang Anda beri komentar w = v. Bagaimana Anda menjelaskannya?
johnbakers
2
Anda tidak bisa, dan tidak, karena Copydimaksudkan untuk diterapkan untuk jenis "murah", seperti u8dalam contoh. Jika Anda menulis tipe yang cukup kelas berat, yang menurut Anda memindahkan lebih efisien daripada salinan, buatlah tidak tersirat Copy. Perhatikan bahwa dalam kasus u8, Anda tidak mungkin bisa lebih efisien dengan pemindahan, karena di bawah kap itu mungkin setidaknya memerlukan salinan pointer - yang sudah semahal salinan u8, jadi mengapa repot-repot.
mdup
Apakah ini berarti bahwa keberadaan Copysifat berdampak pada cakupan variabel seumur hidup implisit? Jika demikian saya pikir itu penting.
Brian Cain
7

Seperti yang sudah tercakup oleh jawaban lain:

  • Copy implisit, murah, dan tidak dapat diimplementasikan kembali (memcpy).
  • Clone eksplisit, mungkin mahal, dan dapat diterapkan kembali secara sewenang-wenang.

Apa yang terkadang hilang dalam pembahasan Copyvs Cloneadalah bahwa hal itu juga mempengaruhi bagaimana kompilator menggunakan perpindahan vs salinan otomatis. Misalnya:

#[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);
}

Contoh pertama ( PointCloneAndCopy) berfungsi dengan baik di sini karena salinan implisit, tetapi contoh kedua ( PointCloneOnly) akan mengalami kesalahan dengan penggunaan setelah pemindahan:

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

Untuk menghindari pemindahan implisit, kita bisa memanggil secara eksplisit let p2 = p1.clone();.

Ini mungkin menimbulkan pertanyaan tentang bagaimana memaksa perpindahan dari tipe yang mengimplementasikan sifat Salin? . Jawaban singkatnya: Anda tidak bisa / tidak masuk akal.

bluenote10
sumber
@Shepmaster Saya menghapusnya meskipun saya merasa jauh lebih mudah dibaca karena berisi kode warna yang bagus dari kompiler Rust dan saya secara khusus memastikan bahwa semua kata pencarian yang relevan juga terdapat dalam teks.
bluenote10