Catatan editor : pertanyaan ini ditanyakan sebelum Rust 1.0 dan beberapa pernyataan dalam pertanyaan tersebut belum tentu benar di Rust 1.0. Beberapa jawaban telah diperbarui untuk menangani kedua versi tersebut.
Saya memiliki struct ini
struct Triplet {
one: i32,
two: i32,
three: i32,
}
Jika saya meneruskan ini ke suatu fungsi, itu secara implisit disalin. Sekarang, terkadang saya membaca bahwa beberapa nilai tidak dapat disalin dan oleh karena itu harus dipindahkan.
Apakah mungkin membuat struct ini Triplet
tidak dapat disalin? Misalnya, apakah mungkin untuk menerapkan sifat yang tidak dapat Triplet
disalin dan oleh karena itu "dapat dipindahkan"?
Saya membaca di suatu tempat bahwa seseorang harus menerapkan Clone
sifat untuk menyalin hal-hal yang tidak dapat disalin secara implisit, tetapi saya tidak pernah membaca tentang sebaliknya, yaitu memiliki sesuatu yang secara implisit dapat disalin dan membuatnya tidak dapat disalin sehingga ia bergerak sebagai gantinya.
Apakah itu masuk akal?
Jawaban:
Kata Pengantar : Jawaban ini ditulis sebelum opt-in built-in sifat -Khusus yang
Copy
aspek -were dilaksanakan. Saya telah menggunakan tanda kutip blok untuk menunjukkan bagian yang hanya diterapkan pada skema lama (yang diterapkan saat pertanyaan diajukan).Tipe sekarang berpindah secara default, yaitu, ketika Anda mendefinisikan tipe baru yang tidak diimplementasikan
Copy
kecuali Anda secara eksplisit mengimplementasikannya untuk tipe Anda:Implementasi hanya bisa ada jika setiap tipe terdapat dalam new
struct
atauenum
itu sendiriCopy
. Jika tidak, kompilator akan mencetak pesan kesalahan. Itu juga hanya bisa ada jika tipe tidak memilikiDrop
implementasi.Untuk menjawab pertanyaan yang tidak Anda tanyakan ... "ada apa dengan gerakan dan salinan?":
Pertama saya akan menentukan dua "salinan" yang berbeda:
(&usize, u64)
, itu adalah 16 byte pada komputer 64-bit, dan salinan dangkal akan mengambil orang-orang 16 byte dan mereplikasi mereka nilai di beberapa bagian memori 16-byte lainnya, tanpa menyentuhusize
di ujung lain dari&
. Artinya, itu setara dengan meneleponmemcpy
.Rc<T>
hanya melibatkan peningkatan jumlah referensi, dan salinan semantik aVec<T>
melibatkan pembuatan alokasi baru, dan kemudian secara semantik menyalin setiap elemen yang disimpan dari yang lama ke yang baru. Ini dapat berupa salinan dalam (mis.Vec<T>
) Atau dangkal (mis.Rc<T>
Tidak menyentuh yang disimpanT
),Clone
secara longgar didefinisikan sebagai jumlah pekerjaan terkecil yang diperlukan untuk menyalin secara semantik nilai tipeT
dari dalam a&T
keT
.Rust seperti C, setiap nilai yang digunakan dari suatu nilai adalah salinan byte:
Mereka adalah salinan byte apakah
T
bergerak atau tidak atau "dapat disalin secara implisit". (Untuk lebih jelasnya, mereka tidak harus benar-benar salinan byte-by-byte pada saat run-time: kompilator bebas untuk mengoptimalkan salinan jika perilaku kode dipertahankan.)Namun, ada masalah mendasar dengan salinan byte: Anda berakhir dengan nilai duplikat di memori, yang bisa sangat buruk jika memiliki destruktor, mis.
Jika
w
hanya salinan byte biasav
maka akan ada dua vektor menunjuk pada alokasi yang sama, keduanya dengan penghancur yang membebaskannya ... menyebabkan bebas ganda , yang merupakan masalah. NB. Ini akan baik-baik saja, jika kita melakukan salinan semantikv
kew
, karena ituw
akan menjadi independennya sendiriVec<u8>
dan penghancurnya tidak akan menginjak-injak satu sama lain.Ada beberapa kemungkinan perbaikan di sini:
w
memiliki alokasinya sendiri, seperti C ++ dengan konstruktor salinannya.v
tidak dapat digunakan lagi dan destruktornya tidak berjalan.Yang terakhir adalah apa yang Rust lakukan: perpindahan hanyalah penggunaan nilai di mana sumber secara statis tidak valid, sehingga kompilator mencegah penggunaan lebih lanjut dari memori yang sekarang tidak valid.
Jenis yang memiliki destruktor harus dipindahkan ketika digunakan oleh nilai (alias ketika byte disalin), karena mereka memiliki manajemen / kepemilikan beberapa sumber daya (misalnya alokasi memori, atau pegangan file) dan sangat tidak mungkin salinan byte akan menduplikasi ini dengan benar kepemilikan.
"Nah ... apa itu salinan implisit?"
Pikirkan tentang tipe primitif seperti
u8
: salinan byte sederhana, cukup salin satu byte, dan salinan semantik sama sederhananya, salin satu byte. Secara khusus, salinan byte adalah salinan semantik ... Rust bahkan memiliki sifat bawaanCopy
yang menangkap tipe mana yang memiliki salinan semantik dan byte yang identik.Karenanya, untuk
Copy
tipe ini penggunaan nilai menurut juga otomatis merupakan salinan semantik, sehingga sangat aman untuk terus menggunakan sumbernya.Seperti disebutkan di atas, sifat bawaan keikutsertaan diimplementasikan, sehingga kompilator tidak lagi memiliki perilaku otomatis. Namun, aturan yang digunakan untuk perilaku otomatis di masa lalu adalah aturan yang sama untuk memeriksa apakah legal untuk diterapkan
Copy
.sumber
Cara termudah adalah dengan menyematkan sesuatu dalam tipe Anda yang tidak dapat disalin.
Pustaka standar menyediakan "tipe penanda" untuk kasus penggunaan ini: NoCopy . Sebagai contoh:
sumber