@rachmandfc_gabung
Nama fitur: pin
Target stabilisasi: 1.32.0
Masalah pelacakan: # 49150
RFC terkait: rust-lang / rfcs # 2349
Ini adalah proposal untuk menstabilkan fitur perpustakaan pin
, membuat "penyematan"
API untuk memanipulasi memori tersemat yang dapat digunakan di stable.
(Saya telah mencoba menulis proposal ini sebagai "laporan stabilisasi" yang komprehensif.)
[std|core]::pin::Pin
Ini menstabilkan tipe Pin
di submodul pin
dari std
/ core
. Pin
adalah
dasar, pembungkus transparan di sekitar tipe generik P
, yang dimaksudkan
menjadi tipe penunjuk (misalnya, Pin<&mut T>
dan Pin<Box<T>>
keduanya
valid, konstruksi yang dimaksudkan). Pin
wrapper mengubah pointer menjadi "pin"
memori yang dirujuk pada tempatnya, mencegah pengguna memindahkan objek keluar
dari memori itu.
Cara biasa untuk menggunakan tipe Pin
adalah dengan membuat varian tersemat dari beberapa
jenis memiliki pointer ( Box
, Rc
, dll). Perpustakaan std memiliki semua petunjuk
menyediakan konstruktor pinned
yang mengembalikan ini. Kemudian, untuk memanipulasi file
nilai di dalamnya, semua petunjuk ini memberikan cara untuk menurunkan ke arah Pin<&T>
dan Pin<&mut T>
. Pointer yang dipasangi pin dapat dilakukan, memberi Anda &T
, tetapi tidak bisa
aman saling deref: ini hanya mungkin menggunakan tidak aman get_mut
fungsi.
Akibatnya, siapa pun yang memutasikan data melalui pin akan diwajibkan untuk menjunjung
berbeda bahwa mereka tidak pernah keluar dari data itu. Ini memungkinkan kode lain untuk
dengan aman berasumsi bahwa data tidak pernah dipindahkan, memungkinkannya berisi (untuk
contoh) referensi diri.
Jenis Pin
akan memiliki API yang distabilkan ini:
impl<P> Pin<P> where P: Deref, P::Target: Unpin
fn new(pointer: P) -> Pin<P>
impl<P> Pin<P> where P: Deref
unsafe fn new_unchecked(pointer: P) -> Pin<P>
fn as_ref(&self) -> Pin<&P::Target>
impl<P> Pin<P> where P: DerefMut
fn as_mut(&mut self) -> Pin<&mut P::Target>
fn set(&mut self, P::Target);
impl<'a, T: ?Sized> Pin<&'a T>
unsafe fn map_unchecked<U, F: FnOnce(&T) -> &U>(self, f: F) -> Pin<&'a U>
fn get_ref(self) -> &'a T
impl<'a, T: ?Sized> Pin<&'a mut T>
fn into_ref(self) -> Pin<&'a T>
unsafe fn get_unchecked_mut(self) -> &'a mut T
unsafe fn map_unchecked_mut<U, F: FnOnce(&mut T) -> &mut U>(self, f: F) -> Pin<&'a mut U>
impl<'a, T: ?Sized> Pin<&'a mut T> where T: Unpin
fn get_mut(self) -> &'a mut T
Sebagian besar ciri yang tersirat pada Pin
cukup hafal, keduanya penting untuk
operasinya:
impl<P: Deref> Deref for Pin<P> { type Target = P::Target }
impl<P: DerefMut> DerefMut for Pin<P> where P::Target: Unpin { }
std::marker::Unpin
Lepas pin adalah sifat otomatis aman yang menyisih dari jaminan penyematan. Jika
target dari penunjuk yang disematkan mengimplementasikan Unpin
, itu aman untuk berubah-ubah
dereferensi untuk itu. Unpin
tipe tidak memiliki jaminan bahwa mereka tidak akan memilikinya
dipindahkan dari Pin
.
Hal ini menjadikannya sebagai ergonomis untuk menangani referensi yang disematkan ke sesuatu itu
tidak berisi referensi mandiri karena akan menangani non-pin
referensi. Jaminan Pin
hanya berlaku untuk jenis kasus khusus seperti
struktur referensi sendiri: tipe tersebut tidak mengimplementasikan Unpin
, jadi mereka memilikinya
pembatasan tipe Pin
.
Implementasi penting dari Unpin
di std:
impl<'a, T: ?Sized> Unpin for &'a T
impl<'a, T: ?Sized> Unpin for &'a mut T
impl<T: ?Sized> Unpin for Box<T>
impl<T: ?Sized> Unpin for Rc<T>
impl<T: ?Sized> Unpin for Arc<T>
Ini mengkodifikasi gagasan bahwa penyematan tidak transitif lintas pointer. Bahwa
adalah, Pin<&T>
hanya menyematkan blok memori aktual yang diwakili oleh T
dalam file
tempat. Pengguna terkadang bingung dengan ini dan mengharapkan sebuah tipe
seperti Pin<&mut Box<T>>
menyematkan data T
pada tempatnya, tetapi hanya menyematkan
memori referensi yang disematkan sebenarnya merujuk ke: dalam hal ini, Box
representasi, yang merupakan penunjuk ke heap.
std::marker::Pinned
Tipe Pinned
adalah ZST yang tidak mengimplementasikan Unpin
; itu memungkinkan Anda untuk
tekan implementasi otomatis Unpin
pada stabil, di mana !Unpin
impls
belum stabil.
Konstruktor ditambahkan ke std smart pointer untuk membuat referensi yang disematkan:
Box::pinned(data: T) -> Pin<Box<T>>
Rc::pinned(data: T) -> Pin<Rc<T>>
Arc::pinned(data: T) -> Pin<Arc<T>>
Selama 9 bulan terakhir, API penyematan telah melalui beberapa iterasi sebagai
kami telah menyelidiki kekuatan ekspresif mereka dan juga kesehatan mereka
jaminan. Sekarang saya akan mengatakan dengan yakin bahwa API penyematan stabil di sini
sehat dan cukup dekat dengan maksimal lokal dalam ergonomi dan
ekspresi; yaitu, siap untuk stabilisasi.
Salah satu masalah tersulit dari pemasangan pin adalah menentukan kapan aman untuk dilakukan
proyeksi pin : yaitu, beralih dari Pin<P<Target = Foo>>
ke a
Pin<P<Target = Bar>>
, di mana Bar
adalah bidang Foo
. Untungnya, kami punya
mampu mengkodifikasi seperangkat aturan yang dapat membantu pengguna menentukan apakah seperti itu
proyeksi aman:
(Foo: Unpin) implies (Bar: Unpin)
: ituFoo
(tipe yang mengandung) adalah Unpin
sementaraBar
(jenis yang diproyeksikan) bukan Unpin
.Bar
tidak pernah dipindahkan selama penghancuran Foo
,Foo
tidak memiliki destruktor, atau destruktornya hati-hatiFoo
(tipe yang memuat) bukan repr(packed)
,Selain itu, std API tidak menyediakan cara yang aman untuk menyematkan objek ke tumpukan.
Ini karena tidak ada cara untuk mengimplementasikannya dengan aman menggunakan API fungsi.
Namun, pengguna dapat memasang pin secara tidak aman ke tumpukan dengan menjaminnya
jangan pernah memindahkan objek lagi setelah membuat referensi yang disematkan.
pin-utils
peti di crates.io berisi makro untuk membantu dengan kedua tumpukan
pinning dan proyeksi pin. Makro penyematan tumpukan dengan aman menyematkan objek ke
tumpukan menggunakan trik yang melibatkan bayangan, sedangkan makro untuk proyeksi ada
yang tidak aman, tetapi menghindari Anda harus menulis boilerplate proyeksi
yang mungkin bisa Anda masukkan kode tidak aman yang salah lainnya.
Unpin
dari awal, hapus pin::Unpin
ekspor ulangSebagai aturan umum, kami tidak mengekspor ulang sesuatu dari banyak tempat di std kecuali
satu adalah supermodul dari definisi sebenarnya (mis. pemendekan
std::collections::hash_map::HashMap
hingga std::collections::HashMap
). Untuk ini
alasannya, ekspor ulang std::marker::Unpin
dari std::pin::Unpin
telah habis
tempat.
Pada saat yang sama, ciri penanda penting lainnya seperti Kirim dan Sinkronisasi disertakan
di awal. Jadi, alih-alih mengekspor ulang Unpin
dari modul pin
, oleh
dengan memasukkan awal kita membuatnya tidak perlu untuk mengimpor std::marker::Unpin
,
alasan yang sama itu dimasukkan ke dalam pin
.
Saat ini, banyak fungsi terkait dari Pin
tidak menggunakan sintaks metode.
Secara teori, ini untuk menghindari konflik dengan metode batin yang dapat dipercaya. Namun,
aturan ini belum diterapkan secara konsisten, dan sebagian besar menurut pengalaman kami
hanya membuat segalanya menjadi lebih tidak nyaman. Pointer yang dipasangi pin hanya menerapkan immutable
deref, bukan deref yang bisa berubah atau deref berdasarkan nilai, membatasi kemampuan untuk melakukan deref
bagaimanapun. Selain itu, banyak dari nama ini cukup unik (mis. map_unchecked
)
dan tidak mungkin konflik.
Sebagai gantinya, kami lebih memilih untuk memberikan metode Pin
prioritas mereka; pengguna yang
perlu mengakses metode interior selalu dapat menggunakan UFCS, seperti yang akan mereka lakukan
diperlukan untuk mengakses metode Pin jika kami tidak menggunakan sintaks metode.
get_mut_unchecked
menjadi get_unchecked_mut
Pengurutan saat ini tidak konsisten dengan penggunaan lain di pustaka standar.
impl<P> Unpin for Pin<P>
Implikasi ini tidak dibenarkan oleh pembenaran standar kami untuk unpin impls: tidak ada arah penunjuk antara Pin<P>
dan P
. Kegunaannya tercakup dalam impls for pointers itu sendiri.
Implikasi masa depan ini perlu diubah untuk menambahkan P: Unpin
terikat.
Pin
sebagai repr(transparent)
Pin harus berupa pembungkus transparan di sekitar penunjuk di dalamnya, dengan representasi yang sama.
API pin penting untuk memanipulasi bagian memori dengan aman
dijamin tidak akan dipindahkan. Jika objek dalam memori itu tidak
menerapkan Unpin
, alamat mereka tidak akan pernah berubah. Ini diperlukan untuk
membuat generator referensi sendiri dan fungsi asynchronous. Hasil dari,
tipe Pin
muncul di pustaka standar future
API dan akan segera
muncul di API untuk generator juga (# 55704).
Menstabilkan tipe Pin
dan API-nya merupakan prekursor yang diperlukan untuk menstabilkan
API Future
, yang merupakan prekursor yang diperlukan untuk menstabilkan
async/await
sintaks dan memindahkan seluruh ekosistem IO futures 0.3
async
ke Rust yang stabil.
cc @cramertj @RalfJung
@rachmuhammadfc gabung
Anggota tim @withoutboats telah mengusulkan untuk menggabungkan ini. Langkah selanjutnya adalah meninjau oleh anggota tim yang diberi tag lainnya:
Kekhawatiran:
Setelah mayoritas peninjau menyetujui (dan paling banyak 2 persetujuan masih berlaku), ini akan memasuki periode komentar terakhir. Jika Anda melihat masalah besar yang belum pernah diangkat dalam proses ini, silakan angkat bicara!
Lihat dokumen ini untuk info tentang perintah yang dapat diberikan anggota tim kepada saya.
Terima kasih untuk tulisan mendetail di sini @withoutboats! Saya juga agak bingung secara historis dengan berbagai jaminan Pin
, dan saat ini saya punya beberapa pertanyaan tentang API yang distabilkan di sini tentang jaminan keamanan. Untuk membantu menyelesaikan masalah ini di kepala saya sendiri meskipun saya pikir saya akan mencoba menuliskan hal-hal ini.
Dalam mencoba untuk mulai menulis ini meskipun saya terus berlari ke dinding "apa Unpin
?" Saya agak bingung dengan apa itu dan berbagai jaminan di sekitarnya. Dapatkah Anda mengatakan lagi apa artinya T
dan juga tidak menerapkan Unpin
? Juga, jika Unpin
adalah sifat yang aman untuk menerapkannya secara naif, sepertinya itu dapat digunakan untuk dengan mudah merusak jaminan tidak aman dari Pin<T>
, tetapi saya pasti melewatkan sesuatu
jika saya sudah mendapatkan ini dengan benar, setiap jenis yang bukan referensi sendiri (yaitu: bukan generator) adalah Lepas pin
Ini bukan hanya referensi diri, ada beberapa kasus penggunaan lain untuk alamat memori stabil yang juga dapat didukung oleh Pin
. Mereka relatif sedikit dan jarang.
Bagaimana saya memahami Unpin
aman untuk diterapkan adalah bahwa dengan menerapkannya Anda mungkin melanggar invarian yang diperlukan dari kode tidak aman lainnya yang telah Anda tulis (yang terpenting, hanya Anda yang dapat menulis kode tidak aman ini, tidak ada kode eksternal yang dapat bergantung pada apakah Anda telah menerapkan Unpin
). Tidak ada yang dapat Anda lakukan dengan API aman Pin
yang akan menyebabkan ketidaknyamanan apakah Anda telah mengimplementasikan Unpin
. Dengan memilih untuk menggunakan beberapa API yang tidak aman Pin
Anda menjamin bahwa Anda hanya akan menerapkan Unpin
jika aman untuk melakukannya. Itu tercakup dalam poin 1 dari bagian "Catatan tentang penyematan & keamanan" di atas.
Hm saya masih belum begitu mengerti Unpin
. Pada awalnya saya hanya mencoba untuk memahami apa artinya menerapkan atau tidak menerapkan Unpin
.
Pertama, mungkin berguna untuk mengetahui jenis apa yang menerapkan Unpin
secara otomatis! Disebutkan di atas bahwa jenis penunjuk umum (Arc / Rc / Box / referensi) mengimplementasikan Unpin
, tapi saya pikir hanya itu? Jika ini adalah sifat otomatis, apakah itu berarti bahwa tipe MyType
secara otomatis mengimplementasikan Unpin
jika hanya berisi pointer? Atau apakah tidak ada tipe lain yang secara otomatis menerapkan Unpin
?
Saya terus mencoba untuk meringkas atau menyatakan apa jaminan Unpin
dan semacamnya, tetapi saya merasa sangat sulit untuk melakukannya. Dapatkah seseorang membantu dengan mengulangi lagi apa artinya menerapkan Unpin
serta apa artinya tidak menerapkan Unpin
?
Saya rasa saya memahami jaminan Pin<P>
mana Anda tidak dapat keluar dari salah satu anggota inline P::Target
, tetapi apakah itu benar?
@alexcrichton Terima kasih atas pertanyaannya, saya yakin API
Pertama, mungkin berguna untuk mengetahui jenis apa yang menerapkan Lepas pin secara otomatis!
Lepas pin adalah sifat otomatis seperti Kirim atau Sinkronkan, jadi sebagian besar jenis menerapkannya secara otomatis. Generator dan jenis fungsi asinkron adalah !Unpin
. Tipe yang bisa berisi generator atau badan fungsi async (dengan kata lain, tipe dengan parameter tipe) juga tidak otomatis Unpin
kecuali parameter tipe mereka adalah.
Implikasi eksplisit untuk tipe pointer adalah membuat Unpin
meskipun parameter tipenya tidak. Alasan untuk ini semoga akan lebih jelas pada akhir komentar ini.
Dapatkah seseorang membantu dengan mengulangi lagi apa artinya mengimplementasikan Lepas Pin serta apa artinya tidak menerapkan Lepas Pin?
Berikut adalah ide dasar dari pinning API. Pertama, diberikan tipe pointer P
, Pin<P>
bertindak seperti P
kecuali jika P::Target
mengimplementasikan Unpin
, tidak aman untuk mengubah referensi Pin<P>
. Kedua, ada dua kode tidak aman invarian dasar yang terkait dengan Pin
harus dipelihara:
&mut P::Target
dari Pin<P>
, Anda tidak boleh memindahkan P::Target
.Pin<P>
, harus dijamin bahwa Anda tidak akan pernah bisa mendapatkan pointer yang tidak disematkan ke data yang ditunjuk oleh pointer sampai destruktor berjalan.Implikasi dari semua ini adalah bahwa jika Anda membuat Pin<P>
, nilai yang ditunjukkan oleh P
tidak akan pernah bergerak lagi, yang merupakan jaminan yang kami butuhkan untuk struct referensi sendiri dan juga mengganggu. koleksi. Tetapi Anda dapat memilih keluar dari jaminan ini hanya dengan menerapkan Unpin
untuk tipe Anda.
Jadi jika Anda menerapkan Unpin
untuk suatu tipe, Anda mengatakan bahwa tipe tersebut memilih keluar dari jaminan keamanan tambahan sebesar Pin
- mungkin untuk saling membedakan petunjuk yang menunjuk ke sana. Ini berarti Anda mengatakan bahwa tipe tidak perlu tidak bergerak untuk digunakan dengan aman.
Memindahkan tipe pointer seperti Rc<T>
tidak akan memindahkan T
karena T
berada di belakang pointer. Demikian pula, menyematkan pointer ke Rc<T>
(seperti dalam Pin<Box<Rc<T>>
) sebenarnya tidak menyematkan T
, itu hanya menyematkan pointer tertentu. Inilah sebabnya mengapa apapun yang menyimpan generiknya di belakang pointer dapat mengimplementasikan Unpin
bahkan ketika obat generik mereka tidak.
Juga, jika Lepas Pin adalah sifat yang aman untuk menerapkannya secara naif, sepertinya itu dapat digunakan untuk dengan mudah merusak jaminan Pin yang tidak aman.
, tapi aku pasti melewatkan sesuatu
Ini adalah salah satu bagian tersulit dari API pemasangan pin, dan kami salah pada awalnya.
Lepas pin berarti "bahkan setelah sesuatu dimasukkan ke dalam pin, aman untuk mendapatkan referensi yang bisa berubah ke pin itu." Ada ciri lain yang ada saat ini yang memberi Anda akses yang sama: Drop
. Jadi apa yang kami temukan adalah karena Drop
aman, Unpin
juga harus aman. Apakah ini merusak seluruh sistem? Tidak terlalu.
Untuk benar-benar mengimplementasikan tipe referensi sendiri akan memerlukan kode yang tidak aman - dalam praktiknya, satu-satunya tipe referensi mandiri yang dipedulikan siapa pun adalah tipe yang dibuat oleh compiler untuk Anda: generator dan mesin status fungsi asinkron. Ini secara eksplisit mengatakan mereka tidak mengimplementasikan Unpin
dan mereka tidak memiliki implementasi Drop
, jadi Anda tahu, untuk tipe ini, setelah Anda memiliki Pin<&mut T>
, mereka akan tidak pernah benar-benar mendapatkan referensi yang bisa berubah, karena mereka adalah tipe anonim yang kami tahu tidak menerapkan Lepas Pin atau Lepas.
Masalahnya muncul setelah Anda memiliki struct yang berisi salah satu dari jenis anonim ini, seperti kombinator masa depan. Untuk beralih dari Pin<&mut Fuse<Fut>>
menjadi Pin<&mut Fut>
, Anda harus melakukan "proyeksi pin". Di sinilah Anda dapat mengalami masalah: jika Anda menyematkan proyek ke bidang kombinator di masa mendatang, tetapi kemudian menerapkan Jatuhkan untuk kombinator, Anda dapat keluar dari bidang yang seharusnya disematkan.
Karena alasan ini, proyeksi pin tidak aman! Untuk melakukan proyeksi pin tanpa melanggar invarian pin, Anda harus menjamin bahwa Anda tidak pernah melakukan beberapa hal, yang saya cantumkan dalam proposal stabilisasi.
Jadi, tl; dr: Drop
ada, jadi Unpin
pasti aman. Tetapi ini tidak merusak semuanya, itu hanya berarti bahwa proyeksi pin adalah unsafe
dan siapa pun yang ingin memasang pin pada proyek perlu mempertahankan satu set invarian.
generator dan mesin status fungsi async. Ini secara eksplisit mengatakan bahwa mereka tidak mengimplementasikan Lepas Pin dan tidak memiliki implementasi Jatuhkan, jadi Anda tahu, untuk jenis ini, setelah Anda memiliki Pin <& mut T>, mereka tidak akan pernah benar-benar mendapatkan referensi yang bisa berubah, karena mereka jenis anonim yang kami tahu tidak menerapkan Lepas Pin atau Lepas.
Bukankah mesin status async memiliki implementasi Drop
? Hal-hal yang ada di "tumpukan" dari fungsi asinkron (yang mungkin sama dengan bidang di mesin status) perlu dihancurkan ketika fungsi asinkron selesai atau dibatalkan. Atau apakah ini terjadi sebaliknya?
Saya rasa yang penting dalam konteks ini adalah apakah item impl Drop for Foo {…}
ada, yang akan menjalankan kode dengan &mut Foo
yang dapat digunakan misalnya mem::replace
untuk "exfiltrate" dan memindahkan Foo
nilai.
Ini tidak sama dengan "lem jatuh", yang dapat disebut melalui ptr::drop_in_place
. Lem tetes untuk tipe Foo
akan memanggil Drop::drop
jika diimplementasikan, kemudian secara rekursif memanggil lem tetes untuk setiap bidang. Tetapi panggilan rekursif itu tidak pernah melibatkan &mut Foo
.
Selain itu, sementara generator (dan karenanya mesin status asinkron) memiliki lem drop kustom, itu hanya untuk menjatuhkan kumpulan bidang yang benar berdasarkan keadaan saat ini, ia berjanji untuk tidak pernah memindahkan bidang apa pun selama penurunan.
Terminologi yang saya gunakan (meskipun saya tidak berpikir ada standar apa pun): "lem jatuh" adalah kompiler yang dihasilkan berjalannya bidang rekursif, memanggil destruktornya; "Implementasi Jatuhkan" adalah implementasi dari sifat Drop
, dan "destruktor" adalah kombinasi dari lem jatuh dan implementasi Jatuhkan. Lem drop tidak pernah memindahkan apapun, jadi kami hanya peduli tentang implementasi Drop.
Apakah seseorang ingin menulis bab nomicon tentang masa depan? Tampaknya sangat penting mengingat betapa halusnya hal ini. Secara khusus saya pikir contoh, terutama contoh implementasi buggy / buruk dan bagaimana mereka tidak sehat, akan mencerahkan.
@Gankro Tentu, Anda bisa menurunkan saya untuk itu.
Terima kasih atas penjelasannya semuanya!
Saya pribadi sangat baru dalam async Rust dan Pin API, tapi saya telah bermain-main dengannya selama beberapa hari terakhir (https://github.com/rust-lang-nursery/futures-rs/pull/1315 - di mana Saya mencoba mengeksploitasi pinning untuk koleksi yang mengganggu dalam kode async).
Selama percobaan, saya mendapat beberapa masalah dengan API ini:
impl core::future::future::Future+futures_core::future::FusedFuture
, ciri std::marker::Unpin
tidak diterapkan untuk std::marker::Pinned
Pin
proyeksi), mencoba menyalin metode dari pin-utils
, sampai saya menemukan solusi yang menggunakan 2 metode tidak aman untuk melakukan apa yang saya butuhkan .async
dan kasus select
. Ternyata saya perlu pin_mut!()
itu untuk tumpukan berjangka. Yang sebenarnya bukan tumpukan, yang membuatnya membingungkan.drop()
sekarang lagi mengambil &mut self
, bukan Pin
.Pin
dan Unpin
. Ini sepertinya merupakan kebocoran detail implementasi untuk beberapa kasus penggunaan yang lebih khusus.drop()
dipanggil. Yang memberinya semacam masa virtual antara cakupan saat ini dan 'static
. Tampaknya membingungkan memiliki 2 konsep yang saling terkait, tetapi masih sangat berbeda. Saya bertanya-tanya apakah itu dapat dimodelkan dengan sesuatu seperti 'pinned
.Saat mendapatkan sesuatu untuk dikompilasi, saya sering memikirkan tentang C ++, di mana sebagian besar masalah ini dihindari: Jenis dapat dinyatakan tidak dapat dipindahkan dengan menghapus konstruktor pemindahan dan operator penugasan. Ketika sebuah tipe tidak bisa dipindahkan, tipe yang berisi tipe itu juga tidak. Dengan demikian properti dan persyaratan mengalir secara native melalui hierarki tipe dan diperiksa oleh compiler, daripada meneruskan properti di dalam beberapa panggilan (tidak semua, misalnya bukan drop()
). Saya pikir ini jauh lebih mudah untuk dipahami. Tapi mungkin tidak berlaku untuk Rust, karena Future
s saat ini harus sering dipindahkan sebelum sesuatu mulai melakukan polling? Tapi di sisi lain itu mungkin bisa diperbaiki dengan definisi baru dengan sifat itu, atau memisahkan fase perpindahan dan polling.
Mengenai Alex Unpin
komentar: Saya perlahan-lahan memahami apa artinya Unpin
, tetapi saya setuju bahwa itu sulit untuk dipahami. Mungkin nama lain bisa membantu, tapi saya tidak bisa menemukan yang ringkas sekarang. Sesuatu ThingsInsideDontBreakIfObjectGetsMoved
, DoesntRequireStableAddress
, PinDoesntMatter
, dll.
Apa yang belum sepenuhnya saya pahami adalah, mengapa mendapatkan &mut self
dari Pin<&mut self>
tidak aman untuk semua jenis. Jika Pin hanya berarti alamat objek itu sendiri stabil, maka ini harus berlaku untuk sebagian besar tipe Rust. Tampaknya ada masalah lain yang diintegrasikan ke dalam Pin, yaitu bahwa tipe referensi mandiri di dalamnya tidak rusak. Untuk tipe yang memanipulasi mereka setelah memiliki Pin
selalu tidak aman. Tapi saya pikir dalam banyak kasus itu akan dihasilkan oleh kompiler, dan tidak aman atau bahkan petunjuk mentah seharusnya baik-baik saja. Untuk kombinator masa depan secara manual yang perlu meneruskan panggilan yang disematkan ke subbidang, jelas perlu ada panggilan tidak aman untuk membuat pin ke bidang sebelum melakukan polling. Tetapi dalam kasus itu saya melihat ketidakamanan lebih terkait dengan tempat yang sebenarnya penting (apakah pin masih valid?) Daripada mengakses bidang lain yang tidak menjadi masalah sama sekali.
Pemikiran lain yang saya miliki adalah apakah mungkin untuk mendapatkan sistem yang lebih sederhana jika beberapa batasan diterapkan. Misalnya jika hal-hal yang memerlukan penyematan hanya dapat digunakan di dalam metode asinkron dan tidak dengan kombinator masa depan. Mungkin itu bisa menyederhanakan sesuatu menjadi salah satu dari Pin
atau Unpin
. Mempertimbangkan bahwa untuk banyak metode kode async / await dengan dukungan pilih / gabung mungkin menguntungkan bagi kombinator, ini mungkin bukan kerugian terbesar. Tetapi saya belum cukup memikirkan apakah ini benar-benar membantu.
Sisi positifnya: Mampu menulis kode async / await dengan pinjaman "stack" cukup keren dan sangat dibutuhkan! Dan kemampuan untuk menggunakan pemasangan pin untuk kasus penggunaan lain, seperti koleksi yang mengganggu, akan membantu performa serta target seperti sistem atau kernel yang disematkan. Jadi saya sangat menantikan solusi untuk ini.
Sedikit nit wrt laporan stabilisasi:
fn get_unchecked_mut(self) -> &'a mut T
Saya menduga ini sebenarnya adalah unsafe fn
?
@ Matthias247 Anda tidak dapat dengan aman mendapatkan & mut T dari Pin<P<T>>
jika T bukan Lepas pin karena Anda dapat mem::swap
untuk mengeluarkan T dari Pin, yang akan menggagalkan tujuan menyematkan sesuatu.
Satu hal yang sulit saya jelaskan kepada diri saya sendiri adalah apa yang membuat Future berbeda secara fundamental dari ciri-ciri lain sehingga Pin perlu menjadi bagian dari API-nya. Maksud saya, saya tahu secara intuitif itu karena async / await memerlukan penyematan, tetapi apakah itu menjelaskan secara spesifik tentang Futures yang berbeda dari, katakanlah, Iterator?
Bisakah jajak pendapat mengambil & mut sendiri dan hanya menerapkan Future untuk jenis Pin<P>
atau jenis yang Lepas pin?
Bagaimana saya bisa mengakses bidang yang bisa berubah dalam metode saya yang menggunakan Pin sebagai parameter.
Ini benar-benar membuat saya bertanya-tanya apakah ada beberapa metode yang hilang pada Pin
,
impl<'a, T: ?Sized> Pin<&'a T> {
fn map<U: Unpin, F: FnOnce(&T) -> &U>(self, f: F) -> Pin<&'a U>
}
impl<'a, T: ?Sized> Pin<&'a mut T> {
fn map<U: Unpin, F: FnOnce(&mut T) -> &mut U>(self, f: F) -> Pin<&'a mut U>
}
Ini dapat digunakan di makro pin_utils::unsafe_unpinned!
.
Saya mencoba mencari _mengapa_ makro ini mengklaim tidak aman. Jika kami !Unpin
dan memiliki bidang Unpin
, mengapa tidak aman untuk memproyeksikan bidang ini?
Satu-satunya situasi di mana saya dapat melihatnya tidak sehat adalah mengimplementasikan tipe kustom !Unpin
, mengambil pointer mentah ke bidang Unpin
diri (dan mengandalkannya memiliki alamat yang stabil / menunjuk ke instance yang sama), lalu memperoleh &mut
ke bidang yang sama dan meneruskannya ke fungsi eksternal. Ini tampaknya berada di bawah alasan yang sama mengapa menerapkan Unpin
itu aman, dengan mengambil pointer mentah ke bidang !Unpin
Anda memilih untuk tidak dapat memanggil beberapa brankas Lebah.
Untuk membuat situasi sebelumnya lebih aman mungkin ada pembungkus yang dibangun di atas Pinned
untuk menandai yang biasanya Unpin
bidang struct sebenarnya !Unpin
daripada hanya menambahkan Pinned
ke struct secara keseluruhan:
pub struct MustPin<T: Unpin>(T, Pinned);
impl<T: Unpin> MustPin<T> {
pub const fn new(t: T) -> Self { ... }
pub fn get(self: Pin<&Self>) -> *const T { ... }
pub fn get_mut(self: Pin<&mut Self>) -> *mut T { ... }
}
Ini semua tampaknya kompatibel dengan API saat ini, ini dapat menghapus beberapa ketidakamanan dari futures-rs
combinators (misalnya akan memberikan akses yang aman ke bidang tambahan seperti ini ), tetapi tidak diperlukan untuk penggunaan saat ini. Kami mungkin dapat bereksperimen dengan beberapa API seperti ini untuk menerapkan jenis !Unpin
(seperti koleksi yang mengganggu) dan menambahkannya nanti.
@ Nemo157 Fungsi peta tersebut tidak aman karena saya bisa mem::swap
pada &mut T
fungsi melewati saya sebelum mengembalikan &mut U
. (baik yang bisa berubah adalah, yang tidak bisa diubah mungkin tidak aman)
EDIT: Juga makro pin-utils berbeda, unsafe_unpinned
tidak ada hubungannya dengan tipe target menjadi Unpin
, ini hanya sebuah "proyeksi yang tidak disematkan" - proyeksi ke &mut Field
. Ini aman digunakan selama Anda tidak pernah menyematkan proyek ke bidang itu, meskipun bidang itu !Unpin
.
Satu hal yang sulit saya jelaskan kepada diri saya sendiri adalah apa yang membuat Future berbeda secara fundamental dari ciri-ciri lain sehingga Pin perlu menjadi bagian dari API-nya. Maksud saya, saya tahu secara intuitif itu karena async / await memerlukan penyematan, tetapi apakah itu menjelaskan secara spesifik tentang Futures yang berbeda dari, katakanlah, Iterator?
Tidak ada perbedaan teoretis, tetapi ada yang pragmatis. Pertama, Iterator
stabil. Tetapi sebagai hasilnya, kecuali kita menemukan sesuatu yang sangat pintar, Anda tidak akan pernah bisa menjalankan loop for
melalui generator referensi sendiri tanpa tipuan ganda, meskipun itu seharusnya aman sepenuhnya tanpanya ( karena loop for akan mengkonsumsi dan tidak pernah menggerakkan generator).
Perbedaan pragmatis penting lainnya adalah bahwa pola kode antara Iterator
dan Future
sangat berbeda. Anda tidak dapat pergi 10 menit tanpa ingin meminjam melintasi menunggu titik di masa depan, itulah sebabnya di futures 0.1 Anda melihat Arc
s ini muncul di semua tempat sehingga Anda dapat menggunakan variabel yang sama dalam dua yang berbeda and_then
panggilan. Tapi kita sudah cukup jauh tanpa bisa mengekspresikan iterator referensi sendiri sama sekali, hanya saja kasus penggunaan tidak terlalu penting.
Fungsi peta tersebut tidak aman karena saya bisa
mem::swap
pada&mut T
fungsi melewati saya sebelum mengembalikan&mut U
Ah, sial, lupakan bagian itu: cemberut:
fn as_mut(&mut self) -> Pin<&mut P::Target>
fn into_ref (self) -> Pin <& 'a T> `
Haruskah ini disebut as_pinned_mut
dan into_pinned_ref
, untuk menghindari kebingungan mereka dengan metode yang sebenarnya mengembalikan referensi?
Implementasi penting dari Lepas Pin di std:
Kami juga memiliki impl<P> Unpin for Pin<P> {}
.Untuk tipe yang kita gunakan ini tidak berpengaruh dan sepertinya sedikit lebih aman?
Tidak apa-apa, Anda memiliki itu di daftar Anda. ;)
Saya pikir kita harus mengkodifikasi jaminan Drop
sebelum stabilisasi, atau akan terlambat:
Adalah ilegal untuk membatalkan penyimpanan objek yang disematkan (target referensi
Pin
) tanpa memanggildrop
pada objek.
"Invalidate" di sini bisa berarti "deallocation", tetapi juga "repurposing": Saat mengubah x
dari Ok(foo)
menjadi Err(bar)
, penyimpanan foo
menjadi tidak valid .
Ini memiliki konsekuensi, misalnya, membatalkan alokasi Pin<Box<T>>
, Pin<Rc<T>>
atau Pin<Arc<T>>
menjadi ilegal tanpa terlebih dahulu memanggil drop::<T>
.
Deref
, DerefMut
Saya masih merasa sedikit tidak nyaman tentang bagaimana ini mengganti sifat Deref
menjadi "ini adalah penunjuk yang cerdas". Kami juga menggunakan Deref
untuk hal-hal lain, misalnya untuk "warisan". Itu mungkin anti-pola, tapi masih cukup umum digunakan dan, terus terang, itu berguna. : D
Saya pikir ini bukan masalah kesehatan.
Unpin
Steker tak tahu malu: Saya menulis dua posting blog tentang ini, yang mungkin membantu atau tidak tergantung pada seberapa senang Anda membaca sintaks (semi-) formal. ;) API telah berubah sejak saat itu, tetapi ide dasarnya belum.
@ Matthias247 Saya rasa satu masalah yang Anda hadapi adalah bahwa membangun abstraksi yang melibatkan penyematan saat ini hampir selalu membutuhkan kode yang tidak aman. Menggunakan abstraksi itu baik-baik saja, tetapi misalnya, mendefinisikan kombinator masa depan dalam kode aman tidak akan berhasil. Alasannya adalah karena "proyeksi pin" memiliki batasan yang hanya dapat kami periksa dengan aman dengan perubahan kompilator. Misalnya, Anda bertanya
Mengapa metode drop () saya sekarang mengambil & mut self lagi, bukan Pin.
Nah, drop()
sudah tua - sudah ada sejak Rust 1.0 - jadi kita tidak bisa mengubahnya. Kami ingin sekali membuatnya mengambil Pin<&mut Self>
, dan kemudian tipe Unpin
bisa mendapatkan &mut
seperti yang mereka lakukan sekarang, tapi itu adalah perubahan yang tidak kompatibel ke belakang.
Untuk membuat penulisan kombinator masa depan aman, kita memerlukan proyeksi pin yang aman, dan untuk itu kita harus mengubah kompiler, ini tidak dapat dilakukan di perpustakaan. Kita hampir bisa melakukannya di derive
proc_macro, kecuali kita membutuhkan cara untuk menyatakan "tipe ini tidak memiliki implementasi Drop
atau Unpin
".
Saya pikir akan sepadan dengan usaha untuk mencari tahu bagaimana mendapatkan API yang aman untuk proyeksi pin. Namun, itu tidak harus menghalangi stabilisasi: API yang kami stabilkan di sini harus kompatibel dengan proyeksi pin yang aman. ( Unpin
mungkin harus menjadi item lang untuk mengimplementasikan pernyataan di atas, tapi sepertinya tidak terlalu buruk.)
Saya sering berpikir tentang C ++, di mana sebagian besar masalah ini dihindari: Jenis dapat dinyatakan tidak dapat dipindahkan dengan menghapus konstruktor pemindahan dan operator penugasan. Ketika sebuah tipe tidak bisa dipindahkan, tipe yang berisi tipe itu juga tidak.
Ada beberapa upaya untuk menentukan tipe yang tidak dapat dipindahkan untuk Rust. Ini menjadi sangat rumit dengan sangat cepat.
Satu hal penting untuk dipahami adalah bahwa pemasangan pin tidak menyediakan tipe yang T
, Anda dapat memindahkannya ke mana pun Anda mau. Alih-alih tipe yang tidak dapat dipindahkan , kami menggunakan kemampuan Rust untuk mendefinisikan API baru yang dienkapsulasi, karena kami mendefinisikan Pin<&mut T>
), bukan di pointee ( T
). Tidak ada cara bagi tipe untuk mengatakan "Saya tidak bisa dipindahkan selamanya". Namun, ada cara untuk jenis untuk mengatakan "Jika aku disematkan, tidak bergerak lagi". Jadi MyFuture
yang saya miliki akan selalu dapat dipindahkan, tetapi Pin<&mut MyFuture>
adalah penunjuk ke instance MyFuture
yang tidak dapat dipindahkan lagi .
Ini adalah poin yang halus, dan kita mungkin harus meluangkan waktu untuk menyempurnakan dokumen di sini untuk membantu menghindari kesalahpahaman yang sangat umum ini.
Tapi kita sudah cukup jauh tanpa bisa mengekspresikan iterator referensi sendiri sama sekali, hanya saja kasus penggunaan tidak terlalu penting.
Ini karena, sejauh ini, semua iterator ditentukan menggunakan tipe dan blok impl Iterator for …
. Ketika status perlu disimpan di antara dua iterasi, tidak ada pilihan selain menyimpannya di bidang tipe iterator dan bekerja dengan &mut self
.
Namun, meskipun ini bukan motivasi utama untuk menyertakan generator dalam bahasa, alangkah baiknya jika pada akhirnya dapat menggunakan generator yield
sintaks untuk mendefinisikan sesuatu yang dapat digunakan dengan for
loop. Segera setelah itu ada, meminjam yield
karena sama pentingnya untuk generator-iterator seperti halnya generator-futures.
(
Unpin
mungkin harus menjadi item lang untuk mengimplementasikan pernyataan di atas, tapi sepertinya tidak terlalu buruk.)
Unpin
dan Pin
sudah harus menjadi item lang untuk mendukung generator tak bergerak yang aman.
Ok terima kasih atas penjelasannya semua! Saya setuju dengan @Gankro bahwa bab bergaya nomicon tentang Pin
dan semacamnya akan sangat berguna di sini. Saya menduga ada banyak riwayat pengembangan mengapa berbagai metode aman ada dan mengapa metode yang tidak aman tidak aman dan semacamnya.
Dalam upaya untuk membantu diri saya sendiri memahami hal ini meskipun saya ingin mencoba lagi untuk menuliskan mengapa setiap fungsi aman atau mengapa unsafe
. Dengan pemahaman dari atas, saya mendapatkan semua yang berikut, tetapi ada beberapa pertanyaan di sana-sini. Jika orang lain dapat membantu saya mengisi ini, itu bagus! (atau bantu menunjukkan di mana pemikiran saya salah)
fn new(P) -> Pin<P> where P: Deref, P::Target: Unpin
Pin<P>
, dan selama keberadaanPin<P>
biasanya berarti P::Target
tidak akan pernah dipindahkan lagi di fileP::Target
mengimplementasikan Unpin
yang dibaca "jaminanPin
tidak ada lagi ". Akibatnya, keamanan di sini adalah karena tidak adaunsafe fn new_unchecked(P) -> Pin<P> where P: Deref
unsafe
karena P
tidakUnpin
, jadi Pin<P>
harus menjunjung tinggi jaminan ituP::Target
tidak akan pernah bergerak lagi setelah Pin<P>
dibuat.Cara sepele untuk melanggar jaminan ini, jika fungsi ini aman, lihat
Suka:
fn foo<T>(mut a: T, b: T) {
Pin::new_unchecked(&mut a); // should mean `a` can never move again
let a2 = mem::replace(&mut a, b);
// the address of `a` changed to `a2`'s stack slot
}
Akibatnya, terserah pengguna untuk menjamin bahwa Pin<P>
memang berarti
P::Target
tidak pernah dipindahkan lagi setelah konstruksi, jadi unsafe
!
fn as_ref(&Pin<P>) -> Pin<&P::Target> where P: Deref
Pin<P>
kami memiliki jaminan bahwa P::Target
tidak akan pernah bergerak. BegitulahPin
. Akibatnya hal itu berarti begitu&P::Target
, "pointer pintar" lainnya ke P::Target
akan memberikan hal yang sama&Pin<P>
dapat dengan aman diterjemahkan ke Pin<&P::Target>
.Ini adalah metode umum untuk beralih dari Pin<SmartPointer<T>>
menjadi Pin<&T>
fn as_mut(&mut Pin<P>) -> Pin<&mut P::Target> where P: DerefMut
as_ref
atas.Pin
, jadi tidak ada yang bisa dengan mudahPertanyaan : bagaimana dengan DerefMut
impl yang "jahat"? Ini adalah cara yang aman
untuk memanggil DerefMut
yang disediakan pengguna yang mengemas &mut P::Target
secara asli,
mungkin mengizinkannya untuk memodifikasinya juga. Bagaimana ini aman?
fn set(&mut Pin<P>, P::Target); where P: DerefMut
Pin<P>
(dan fakta bahwa kami tidak tahu apa-apaUnpin
), bukankah ini harus menjamin bahwa P::Target
tidak pernah bergerak? Jika kita bisaP::Target
, bukankah ini tidak aman?unsafe fn map_unchecked<U, FnOnce(&T) -> &U>(Pin<&'a T>, f: F) -> Pin<&'a U>
unsafe
jadi pertanyaan utama di sini adalah "mengapa bukan ini...
Pertanyaan :: apa contoh tandingannya di sini? Jika ini aman, apa
contoh yang menunjukkan pelanggaran jaminan Pin
?
fn get_ref(Pin<&'a T>) -> &'a T
Pin<&T>
berarti T
tidak akan pernah bergerak. Mengembalikan &T
T
, jadi ini harus aman dilakukan sambil mempertahankanSatu "mungkin gotcha" di sini adalah mutabilitas interior, bagaimana jika T
RefCell<MyType>
? Ini, bagaimanapun, tidak melanggar jaminan
Pin<&T>
karena jaminan hanya berlaku untuk T
secara keseluruhan, bukan
bidang interior MyType
. Sementara mutabilitas interior bisa berpindah-pindah
internal, itu masih secara fundamental tidak dapat memindahkan seluruh struktur di belakang a
&
referensi.
fn into_ref(Pin<&'a mut T>) -> Pin<&'a T>
Pin<&mut T>
berarti T
tidak akan pernah bergerak. Hasilnya berarti Pin<&T>
unsafe fn get_unchecked_mut(Pin<&'a mut T>) -> &'a mut T
Pin<&mut T>
berarti T
tidak boleh berpindah, jadi ini sepele unsafe
mem::replace
pada hasil untuk memindahkan T
(dengan aman). Ituunsafe
sini "meskipun saya memberi Anda &mut T
, Anda tidak diizinkan untuk melakukannyaT
".unsafe fn map_unchecked_mut<U, F: FnOnce(&mut T) -> &mut U>(Pin<&'a mut T>, f: F) -> Pin<&'a mut U>
unsafe
sini pada dasarnya setidaknya sama seperti di atas, kami&mut T
dengan aman (tidak memerlukan penutupan yang tidak aman) yang bisamem::replace
. Mungkin ada ketidakamanan lain di sini jugafn get_mut(Pin<&'a mut T>) -> &'a mut T where T: Unpin
Unpin
sebuah tipe mengatakan " Pin<&mut T>
tidak memiliki jaminan, itu&mut T
". Akibatnya, tanpa jaminan untuk&mut T
dengan amanimpl<P: Deref> Deref for Pin<P> { type Target = P::Target }
as_ref
diikuti oleh get_ref
, jadiimpl<P: DerefMut> DerefMut for Pin<P> where T::Target: Unpin { }
as_mut
diikuti oleh get_mut
, jadiimpl<T: ?Sized> Unpin for Box<T>
(dan implementasi terkait penunjuk lainnya)
Box<T>
akan menerapkan sifat Unpin
T
menerapkan Unpin
. Implementasi ini di sini mengkodifikasi gen ituT
secara eksplisit tidak mengimplementasikan Unpin
, Box<T>
mengimplementasikan Unpin
.Pertanyaan : Apa contoh dari apa yang secara fundamental memberdayakan ini?
Misalnya, apa yang aman bisa membutuhkan unsafe
jika impl
Tidak ada.
@ Matthias247 Anda tidak bisa mendapatkan & mut T dengan aman dari Pin
> jika T bukan Lepas pin karena Anda dapat mem :: swap untuk mengeluarkan T dari Pin, yang akan menggagalkan tujuan memasang pin.
Terima kasih! Ya, karena swap
bukanlah metode yang tidak aman, ini jelas bermasalah. Tapi bisakah itu diperbaiki dengan menambahkan Unpin
terikat ke swap()
? Karena semua kode hingga sekarang harus Unpin
atau bagaimanapun juga tidak aman, ini seharusnya tidak merusak apa pun.
Saya kira salah satu hal yang paling membingungkan saya adalah bahwa Pin<T>
mengkodifikasikan beberapa jaminan: Bahwa alamat T stabil, serta beberapa jaminan tentang keadaan internalnya (agak beku / tidak dapat diubah di beberapa situasi, tetapi juga tidak juga).
Bagi saya sepertinya memindahkan kode / proyeksi yang tidak aman hanya ke tempat-tempat di mana panggilan berbasis Pin
lebih lanjut diperlukan (misalnya dalam poll
s pada bidang) mungkin lebih baik untuk menangani proyeksi yang tidak aman tersebut di semua kasus. Namun sekarang saya juga menyadari bahwa ada masalah lain dengan itu: Kode yang memiliki akses ke referensi yang bisa berubah dapat memindahkan bidang dengan bebas, dan ketika drop()
kemudian dipanggil dengan aman di bidang ini, itu mungkin rusak karena itu alamat disimpan di tempat lain. Untuk itu, diperlukan kelebihan drop(Pin<T>)
yang dibicarakan.
@RalfJung Terima kasih atas penjelasannya! Saya setuju dengan fakta bahwa saya benar-benar mencoba melakukan sesuatu yang tidak aman secara umum, dan oleh karena itu tidak masalah untuk meminta pemahaman ekstra dari saya. Saya lebih khawatir tentang orang-orang yang ingin menulis kombinator masa depan yang lebih aman secara umum, tetapi sekarang juga mungkin dihadapkan pada semua istilah itu. Jika mereka dapat menulis kombinator tanpa harus memahami Unpin
dan proyeksi pin sama sekali, dan kemudian hanya mendapatkan kombinator yang bekerja dengan cara yang lebih rendah (hanya pada Unpin
futures), itu tampaknya lebih disukai. Karena saya belum mencobanya, saya tidak dapat mengatakan apakah saat ini masalahnya. Saya pikir seseorang masih perlu menambahkan setidaknya Unpin
batas secara manual.
Saya juga memahami fakta bahwa jenis yang tidak dapat dipindahkan berbeda dari jenis pin. Namun saya saat ini lebih fokus pada kasus penggunaan daripada perbedaannya. Dan untuk kasus penggunaan koleksi yang mengganggu, tipe yang tidak dapat dipindahkan bekerja dengan sangat baik tanpa menimbulkan terlalu banyak kerumitan. Untuk masa depan jelas beberapa penelitian akan diperlukan, karena cara mereka dari jenis yang dapat dipindah ke jenis yang tidak dapat dipindah tidak ada. Jika cara itu tidak lebih ergonomis daripada Pin API, juga tidak akan ada kemenangan.
Menambahkan T: Unpin
ke mem::swap
berarti ini tidak dapat digunakan pada jenis tertentu meskipun mereka tidak berada di dalam Pin.
Tapi bisakah itu diperbaiki dengan menambahkan Unpin terikat ke swap ()? Karena semua kode hingga saat ini harus Lepas pin atau tetap tidak aman, tindakan ini tidak akan merusak apa pun.
Itu akan merusak semua kode generik: Jika Anda menulis fungsi di karat stabil hari ini untuk beberapa T
tanpa batasan, Anda dapat memanggil swap
di atasnya. Ini harus terus bekerja.
tipe non-moveable bekerja dengan sangat baik tanpa menimbulkan terlalu banyak kerumitan.
Tidak ada yang mendemonstrasikan cara untuk menambahkan jenis yang tidak dapat dipindahkan ke Rust dengan cara yang kompatibel dengan mundur tanpa kerumitan yang secara signifikan melebihi apa yang diusulkan untuk stabilisasi di sini. Anda harus menemukan beberapa proposal dan diskusi lama ini di repo RFC. Lihat https://github.com/rust-lang/rfcs/pull/1858 untuk satu contoh.
RFC di https://github.com/rust-lang/rfcs/pull/2349 serta seri blog boat yang dimulai di sini akan membantu memberi Anda beberapa latar belakang dan kesan dari desain lain yang dipertimbangkan. (Perhatikan juga tanggalnya, desain ini telah dikerjakan selama hampir 10 bulan!)
Juga mem :: swap adalah ikan haring merah, karena sama sekali bukan fungsi yang menarik. Benar-benar adil
let temp = *a;
*a = *b;
*b = temp;
@Gankro tidak menggunakan kode yang tidak aman? Afaik tidak mungkin menulisnya secara harfiah.
Sunting: Saya kira cara lain untuk berpikir tentang ini adalah menambahkan T: Unpin
ke mem::swap
sebenarnya mengubah definisi keamanan di tingkat bahasa. Itu akan menghancurkan semua mycrate::swap
fns di alam liar.
Menambahkan T: Lepas pin ke mem :: swap berarti ini tidak dapat digunakan pada jenis tertentu meskipun mereka tidak berada di dalam Pin.
Jika Lepas pin secara otomatis diturunkan (dengan cara yang sama seperti Sync
/ Send
adalah) maka saya pikir ini seharusnya tidak menjadi masalah.
Itu akan merusak semua kode generik: Jika Anda menulis sebuah fungsi di karat stabil hari ini untuk beberapa T tanpa batasan, Anda dapat memanggil swap di atasnya. Ini harus terus bekerja.
Tapi yang ini jelas sekali. Tidak memikirkan fakta bahwa batas sifat perlu disebarkan secara eksplisit di Rust.
Tidak ada yang telah mendemonstrasikan cara untuk menambahkan jenis yang tidak dapat dipindahkan ke Rust dengan cara yang kompatibel ke belakang tanpa kerumitan yang secara signifikan melebihi apa yang diusulkan untuk stabilisasi di sini. Anda harus menemukan beberapa proposal dan diskusi lama ini di repo RFC. Lihat rust-lang / rfcs # 1858 untuk satu contoh.
Terima kasih, saya akan membaca sedikit lebih banyak tentang pekerjaan sebelumnya tentang ini jika saya punya waktu. Jelas banyak pemikiran dan upaya telah dilakukan untuk ini, dan saya tentu tidak ingin memblokir banyak hal. Saya hanya ingin menyampaikan kekhawatiran dan pertanyaan saya ketika mencoba menangani hal ini.
@ Matthias
Jika Lepas pin secara otomatis diturunkan (dengan cara yang sama seperti Sync / Send) maka saya pikir ini seharusnya tidak menjadi masalah.
Saya tidak yakin apakah saya jelas. Untuk memperjelas, sangat aman untuk memindahkan sekitar jenis !Unpin
dan dengan demikian sangat aman untuk mem::swap
mereka sekitar. Hanya tidak aman untuk memindahkan tipe T: !Unpin
setelah disematkan, yaitu di dalam Pin<P<T>>
.
Misalnya, dalam kode async / await, Anda dapat memindahkan masa depan yang dikembalikan dari fungsi asinkron sebanyak yang Anda inginkan. Anda hanya berhenti dapat memindahkannya setelah Anda pin_mut!
atau memasukkannya ke dalam Pin<Box<..>>>
atau yang lainnya.
Saya punya beberapa macam pertanyaan tentang ini:
Mengingat pentingnya Pin<T>
untuk async
dan untuk generator dan potensi ketidaknyamanan saat berinteraksi dengan bagian lain Rust (misalnya swap
& replace
), memiliki verifikasi formal (misalnya oleh @jhjourdan atau @RalfJung) telah dilakukan untuk varian API pinning yang diusulkan di sini?
Apakah API ini memberikan jaminan baru tentang mesin abstrak / semantik operasional Rust? Yaitu, jika kita lupa tentang use case sebagai mekanisme pendukung untuk async
& await
/ generator, dapatkah kita meletakkan ini di dalam peti di ekosistem dan itu hanya akan berfungsi mengingat jaminan saat ini kita memberikan?
Jenis API atau jenis penambahan sistem apa yang menjadi tidak mungkin sebagai akibat dari menstabilkan API penyematan? (pertanyaan ini merupakan perpanjangan dari 2.)
Cara apa yang disediakan API yang akan distabilkan dalam hal mengembangkannya, bahasa yang disediakan tipe &pin T
untuk meningkatkan proyeksi lapangan dan semacamnya (yang tampaknya tidak bagus dengan API yang diusulkan).
Saya punya beberapa macam catatan:
Dokumentasi di pustaka standar tampaknya cukup jarang. :(
Saya setuju dengan orang lain bahwa konstruksi pinning cukup melelahkan / kompleks secara mental.
Contoh Unmovable
dalam dokumentasi tampaknya terlalu rumit dan melibatkan unsafe
; ini tampaknya kurang optimal. Dengan inisialisasi bertahap seperti yang diuraikan dalam draf RFC dalam bahasa (yaitu, meningkatkan NLL) sebagai gantinya dapat memberi kita:
struct Unmovable<'a> {
data: String,
slice: &'a str,
}
let um: Unmovable<'_>;
um.data = "hello".to_string();
um.slice = &um.data; // OK! we borrow self-referentially.
drop(um); // ERROR! `um.slice` is borrowing `um.data` so you cannot move `um`.
// You won't be able to take a &mut reference to `um` so no `swap` problems.
Ini tidak melibatkan nol tidak aman dan cukup mudah bagi pengguna untuk menangani ini.
Selain itu, std API tidak menyediakan cara yang aman untuk menyematkan objek ke tumpukan.
Ini karena tidak ada cara untuk mengimplementasikannya dengan aman menggunakan API fungsi.
Bagaimana dengan API seperti ini?
pub fn using_pin<T, R, F>(value: T, f: F) -> R
where F: for<'a> FnOnce(Pin<&'a mut T>) -> R {
pin_mut!(value); // Actual implementation inlines this but the point is this API is safe as long as pin_mut! is safe.
f(value)
}
Saya belum mengikuti perkembangan API pinning terlalu dekat, jadi ini bisa saja disebutkan atau dijelaskan di tempat lain dan saya tidak dapat menemukannya, mohon maaf jika itu masalahnya:
Tipe
Pinned
adalah ZST yang tidak mengimplementasikanUnpin
; itu memungkinkan Anda untuk
tekan implementasi otomatisUnpin
pada stable, dimana!Unpin
impls
belum stabil.
Adakah penjelasan di mana pun tentang mengapa !Unpin
impls tidak dapat dibuat stabil?
Ini hanya aman jika Foo (tipe yang mengandung) tidak repr (dikemas),
karena hal ini menyebabkan bidang dipindahkan untuk menyetel kembali.
Apakah dikemas berarti bidang dapat dipindahkan secara dinamis? Itu agak menakutkan. Apakah kami yakin bahwa llvm tidak akan pernah menghasilkan kode untuk memindahkan bidang dalam keadaan lain? Demikian juga, mungkinkah nilai yang disematkan pada tumpukan dapat dipindahkan oleh llvm?
Terlepas dari kehalusannya, ini sepertinya API yang sangat bagus. Kerja bagus!
@Centril Ralf telah menulis tentang di blognya .
API pin tidak melibatkan perubahan bahasa sama sekali, dan sepenuhnya diterapkan di pustaka standar menggunakan fitur bahasa yang sudah ada sebelumnya. Ini tidak berdampak pada Rust bahasa dan tidak menyita fitur bahasa lainnya.
Pin
sebenarnya hanyalah implementasi cerdas dari salah satu fitur Rust yang paling berharga dan klasik: kemampuan untuk memperkenalkan invarian ke API dengan menandai bagian dari API unsafe
. Pin
membungkus penunjuk untuk membuat satu operasi ( DerefMut
) tidak aman, mengharuskan orang yang melakukannya untuk menegakkan invarian tertentu (bahwa mereka tidak keluar dari referensi), dan mengizinkan kode lain untuk berasumsi bahwa ini tidak akan pernah terjadi. Contoh serupa, jauh lebih tua dari trik yang sama ini adalah String
, yang membuatnya tidak aman untuk meletakkan byte non-UTF8 ke dalam string, memungkinkan kode lain untuk mengasumsikan bahwa semua data dalam String
adalah UTF8.
Apakah ada penjelasan di mana pun tentang mengapa! Unpin impls tidak dapat dibuat stabil?
Implikasi negatif saat ini tidak stabil, sama sekali tidak terkait dengan API ini.
Implikasi negatif saat ini tidak stabil.
🤦♂️ Itu masuk akal, seharusnya sudah memikirkannya sedikit lebih lama. Terima kasih.
@alexcrichton Itulah beberapa analisis hebat dari API ini, kita harus mencoba untuk mempertahankannya di tempat yang lebih baik daripada beberapa komentar yang akan hilang!
Beberapa komentar:
as_mut
: Pertanyaan: bagaimana dengan impl DerefMut yang "jahat"? Ini adalah cara yang aman
untuk memanggil DerefMut yang disediakan pengguna yang mengelompokkan & mut P :: Target secara asli,
mungkin mengizinkannya untuk memodifikasinya juga. Bagaimana ini aman?
Pada dasarnya, ketika Anda memanggil new_unchecked
, Anda membuat janji tentang implementasi Deref
dan DerefMut
untuk tipe ini.
set
: Pin yang Diberikan(dan fakta bahwa kita tidak tahu apa-apa
Lepas pin), bukankah ini harus menjamin bahwa P :: Target tidak pernah bergerak? Jika kita bisa
menginisialisasi ulang dengan P :: Target lain, bukankah seharusnya ini tidak aman?
Atau apakah ini terkait dengan destruktor dan semua itu?
Ini menjatuhkan konten lama dari penunjuk, dan menempatkan konten baru di sana. "Pinned" bukan berarti "tidak pernah jatuh", itu artinya "tidak pernah dipindahkan sampai dijatuhkan". Jadi Menjatuhkan atau menimpanya sambil memanggil drop
tidak masalah. Memanggil drop
sangat penting, itulah jaminan penurunan yang saya sebutkan di atas.
Di mana Anda melihat sesuatu bergerak di sini?
map_unchecked
: Pertanyaan :: apa contoh penghitungnya di sini? Jika ini aman, apa contoh yang menunjukkan pelanggaran jaminan Pin?
Contoh akan dimulai dengan Pin<&&T>
dan menggunakan metode ini untuk mendapatkan Pin<&T>
. Memasang pin tidak "menyebar melalui" referensi.
get_ref
: Satu "mungkin gotcha" di sini adalah mutabilitas interior, bagaimana jika T ada
RefCell?
Memang, ini gotcha, tapi seperti yang Anda amati bukan masalah kesehatan. Apa yang tidak masuk akal adalah memiliki metode yang berubah dari Pin<RefCell<T>>
menjadi Pin<&[mut] T>
. Pada dasarnya, yang terjadi adalah RefCell
tidak menyebarkan pin, kita dapat impl<T> Unpin for RefCell<T>
.
apakah telah dilakukan verifikasi formal (misalnya oleh @jhjourdan atau @RalfJung) untuk varian API pinning yang diusulkan di sini?
Tidak, bukan dari pihak kami. Posting blog yang saya sebutkan di atas berisi beberapa pemikiran tentang bagaimana saya akan mulai memformalkan ini, tetapi kami belum benar-benar melakukannya dan melakukannya. Jika Anda memberi saya mesin waktu atau mahasiswa PhD yang tertarik, kami akan melakukannya. ;)
jika kita lupa tentang use case sebagai mekanisme pendukung untuk async & await / generator, dapatkah kita meletakkan ini di dalam peti di ekosistem dan itu hanya akan berfungsi mengingat jaminan saat ini yang kita berikan?
Itu niatnya.
Jenis API atau jenis penambahan sistem apa yang menjadi tidak mungkin sebagai akibat dari menstabilkan API penyematan? (pertanyaan ini merupakan perpanjangan dari 2.)
Uh, saya tidak tahu bagaimana menjawab pertanyaan itu dengan percaya diri. Ruang penambahan yang mungkin terlalu besar dan juga dimensi yang terlalu tinggi sehingga saya berani membuat pernyataan universal apa pun tentangnya.
Jalan apa yang disediakan API yang akan distabilkan dalam hal mengembangkannya bahasa yang disediakan & jenis pin T untuk meningkatkan proyeksi lapangan dan semacamnya (yang tampaknya tidak bagus dengan API yang diusulkan).
Saya tidak yakin lagi tentang &pin T
, ini tidak cocok dengan generik baru Pin<T>
. Dalam hal menangani proyeksi, kita memerlukan beberapa peretasan untuk mengatakan " Unpin
dan Drop
belum diterapkan untuk jenis ini", lalu kita dapat melakukannya dengan aman dengan makro. Untuk ergonomi tambahan, kami mungkin menginginkan kapabilitas "proyeksi lapangan" generik dalam bahasa tersebut, yang juga mencakup mulai dari &Cell<(A, B)>
hingga &Cell<A>
.
Apakah ada penjelasan di mana pun tentang mengapa! Unpin impls tidak dapat dibuat stabil?
Implikasi negatif AFAIK memiliki beberapa batasan lama, dan jika Anda menambahkan batasan umum padanya, implik tersebut seringkali tidak berfungsi seperti yang Anda kira. (Batas umum terkadang diabaikan begitu saja, atau lebih.)
Mungkin Chalk memperbaiki semua ini, mungkin hanya sebagian, tapi bagaimanapun kita mungkin tidak ingin memblokir ini di Chalk.
Apakah dikemas berarti bidang dapat dipindahkan secara dinamis? Itu agak menakutkan.
Ini adalah satu-satunya cara yang tepat untuk memanggil drop
pada bidang yang dikemas, mengingat bahwa drop
mengharapkan referensi yang selaras.
Apakah kami yakin bahwa llvm tidak akan pernah menghasilkan kode untuk memindahkan bidang dalam keadaan lain? Demikian juga, mungkinkah nilai yang disematkan pada tumpukan dapat dipindahkan oleh llvm?
LLVM menyalin byte di sekitar seharusnya tidak menjadi masalah, karena tidak dapat mengubah perilaku program. Ini tentang konsep tingkat yang lebih tinggi tentang "memindahkan" data, dengan cara yang dapat diamati di Rust (misalnya karena petunjuk ke data tidak lagi mengarah ke sana). LLVM tidak bisa hanya memindahkan data ke tempat lain yang mungkin kita punya petunjuk. Demikian pula, LLVM tidak bisa begitu saja memindahkan nilai pada tumpukan yang alamatnya diambil.
Ok terima kasih @RalfJung , masuk akal! Beberapa pertanyaan lanjutan ...
Ketika Anda mengatakan "tidak pernah pindah sampai dijatuhkan", ini berarti bahwa Drop
dapat dipanggil di mana &mut self
telah pindah dari alamat Pin<&mut Self>
? Destruktor tidak bisa mengandalkan petunjuk interior yang akurat, bukan?
Kekhawatiran saya ketika melihat set
adalah bagaimana kepanikan akan ditangani, tetapi saya pikir ini bukan masalah setelah menggali lebih dalam codegen.
Ketika Anda mengatakan "tidak pernah pindah sampai dijatuhkan", ini berarti bahwa
Drop
dapat dipanggil di mana&mut self
telah pindah dari alamatPin<&mut Self>
? Destruktor tidak bisa mengandalkan petunjuk interior yang akurat, bukan?
@alexcrichton pemahaman saya adalah itu berarti tidak pernah pindah sampai setelah Drop::drop
kembali. Jika tidak, beberapa kasus penggunaan (koleksi yang mengganggu dan setidaknya buffer DMA yang dialokasikan tumpukan) menjadi tidak mungkin.
Destructors dapat mengandalkan sesuatu yang tidak pernah dipindahkan jika mereka dapat membuktikannya sebelumnya dengan Pin
. Misalnya jika mesin negara hanya bisa memasuki keadaan melalui API yang mengharuskannya untuk disematkan, destruktor dapat berasumsi bahwa itu disematkan sebelum dijatuhkan jika dalam keadaan itu.
Kode tidak dapat berasumsi bahwa destruktor nonlokal tidak memindahkan anggota, tetapi jelas Anda dapat berasumsi bahwa destruktor Anda sendiri tidak memindahkan barang karena Andalah yang menulisnya.
Ketika Anda mengatakan "tidak pernah pindah sampai dijatuhkan", ini berarti Jatuhkan dapat dipanggil di mana & mut self telah pindah dari alamat Pin <& mut Self>? Destruktor tidak bisa mengandalkan petunjuk interior yang akurat, bukan?
Maksud saya, data tidak akan pernah berpindah (dalam arti tidak akan pergi ke tempat lain, termasuk tidak dialokasikan) sampai drop
dipanggil. Dan ya, drop
dapat diandalkan untuk menjalankan lokasi yang disematkan, meskipun tipenya tidak dapat mengungkapkannya. drop
harus mengambil Pin<&mut self>
(untuk semua jenis), tapi sayangnya, sudah terlambat untuk itu.
Setelah drop
dipanggil, datanya hanyalah byte yang tidak berarti, Anda dapat melakukan apa saja dengannya: Taruh barang baru di sana, hapus alokasi, apa pun.
Hal ini memungkinkan, misalnya, daftar tertaut yang mengganggu di mana destruktor suatu elemen membatalkan pendaftarannya dengan menyesuaikan penunjuk yang bersebelahan. Kami tahu memori tidak akan hilang tanpa pemanggilan destruktor itu. (Elemen masih bisa bocor , dan kemudian akan tetap berada di daftar tertaut selamanya. Tapi dalam kasus itu akan tetap menjadi memori yang valid sehingga tidak ada masalah keamanan.)
Saya telah membaca semua yang dapat saya temukan tentang Pin
, Unpin
, dan diskusi yang mengarah ke semuanya, dan sementara saya _think_ sekarang saya mengerti apa yang sedang terjadi, ada juga banyak tentang kehalusan tentang arti dari berbagai jenis, serta kontrak yang harus diikuti oleh pelaksana dan pengguna. Sayangnya, saya melihat diskusi yang relatif sedikit tentang hal itu di dokumen untuk std::pin
atau pada Pin
dan Unpin
secara khusus. Secara khusus, saya ingin melihat beberapa konten dari komentar ini:
dimasukkan ke dalam dokumen. Secara khusus:
Pin
hanya melindungi "satu tingkat dalam".Pin::new_unchecked
membatasi implikasi untuk Deref
dan DerefMut
.Pin
dan Drop
.!Unpin
hanya tidak diperbolehkan untuk dipindahkan setelah ditempatkan di Pin
.Pin<Box<T>>: Unpin where T: !Unpin
. Ini terkait kembali dengan batasan "satu tingkat dalam" di atas, tetapi menurut saya contoh konkret dengan penjelasan yang tepat ini akan membantu pembaca.Saya menemukan contoh balasan @alexcrichton juga cukup membantu. Memberi pembaca gambaran tentang apa yang bisa salah untuk metode unsafe
yang berbeda di luar prosa saya pikir akan banyak membantu (setidaknya itu pasti berhasil untuk saya). Secara umum, karena kehalusan API ini, saya ingin melihat bagian Keamanan dari berbagai metode tidak aman diperluas, dan mungkin juga dirujuk dari dokumen level modul std::pin
.
Saya sendiri belum banyak menggunakan API ini, jadi saya akan mempercayai penilaian teknis bahwa ini dalam keadaan baik sekarang. Namun, menurut saya nama Pin
, Pinned
dan Unpin
terlalu mirip dan tidak ekspresif untuk jenis / sifat yang sangat berbeda dan API yang relatif kompleks, yang membuat lebih sulit untuk memahaminya (dibuktikan dengan beberapa komentar di utas ini).
Mereka tampaknya mengikuti konvensi penamaan untuk ciri-ciri penanda, jadi saya tidak bisa mengeluh, tapi saya bertanya-tanya apakah kita bisa membuat tradeoff antara verbositas dan nama yang menjelaskan sendiri:
Pinned
- agak membingungkan karena katanya sama dengan Pin
. Mengingat bahwa itu digunakan seperti PhantomData
untuk menambah struct dengan informasi meta yang akan hilang jika tidak, bagaimana dengan PinnedData
, PhantomPinned
, PhantomSelfRef
atau bahkan DisableUnpin
? Sesuatu yang menunjukkan pola penggunaan dan / atau efek yang akan dimilikinya.Unpin
- Seperti di https://github.com/rust-lang/rust/issues/55766#issuecomment -437266922, saya bingung dengan nama Unpin
, karena "lepas pin "dapat dipahami dengan berbagai cara yang ambigu. Sesuatu seperti IgnorePin
, PinNeutral
mungkin?Secara umum saya gagal dan menemukan nama alternatif yang bagus sendiri ...
PhantomPin dan PinNeutral menurut saya adalah nama yang sangat bagus.
Hanya untuk memberikan tandingan, saya telah menemukan Unpin
intuitif (setelah saya memahaminya). Pinned
lebih sulit untuk dipertahankan, karena saya belum pernah menggunakannya dalam kode saya sendiri. Bagaimana dengan mengubah Pinned
menjadi NotUnpin
?
Saya setuju bahwa menemukan nama yang lebih baik mungkin merupakan latihan bikeshedding yang bermanfaat untuk memudahkan pembicaraan tentang pemasangan pin. Saya melamar:
Pin
-> Pinned
: Ketika Anda diberi Pin
, itu benar-benar janji bahwa apa yang Anda berikan _telah disematkan_, dan _ tetap_ disematkan selamanya. Saya tidak merasa terlalu kuat tentang hal ini, karena Anda _bisa_ juga berbicara tentang diberi "pin nilai". Pinned<Box<T>>
lebih baik bagi saya, terutama karena hanya Box
yang disematkan, bukan isinya.Unpin
-> Repin
: untuk ciri-ciri penanda lainnya, kita biasanya membicarakan tentang apa yang dapat Anda lakukan dengan sesuatu yang memiliki ciri penanda itu. Yang mungkin mengapa Unpin
dipilih di tempat pertama. Namun, saya pikir apa yang benar-benar kami ingin pembaca ambil di sini adalah sesuatu yang Unpin
dapat disematkan, dan kemudian disematkan kembali di tempat lain, tanpa konsekuensi atau pertimbangan. Saya juga menyukai saran @ mark-im tentang PinNeutral
, meskipun agak lebih bertele-tele.Pinned
-> PermanentPin
: Menurut saya Pinned
bukan nama yang bagus, karena sesuatu yang mengandung Pinned
tidak benar-benar disematkan .. Itu bukan hanya Unpin
. PhantomPin
memiliki masalah serupa karena mengacu pada Pin
, ketika Pin
bukanlah hal yang ingin Anda capai. NotUnpin
memiliki double negative, yang membuatnya sulit untuk dipikirkan. Sugesti @Kimundi sebesar PhantomSelfRef
cukup mendekati, meskipun menurut saya ini sedikit "rumit", dan ini mengikat properti "tidak dapat dipindahkan setelah disematkan" dengan satu contoh di mana itu terjadi (ketika Anda memiliki referensi sendiri). Saran saya juga bisa diutarakan sebagai PermanentlyPinned
; Saya tidak tahu bentuk mana yang kurang buruk.Saya pikir Pinned
seharusnya menjadi NotX
dimana X
adalah apapun yang akhirnya dinamai Unpin
. Pekerjaan satu-satunya dari Pinned
adalah membuatnya sehingga tipe penutup tidak mengimplementasikan Unpin
. (Edit: dan jika kita mengubah Unpin
ke sesuatu yang lain, mungkin double negative tidak menjadi masalah)
Repin
tidak masuk akal bagi saya, karena "Jenis ini dapat disematkan di tempat lain" hanyalah efek samping dari "Jenis ini dapat dipindahkan dari pin".
@tikue Dalam beberapa hal saya merasakan hal yang sama, tetapi sebaliknya. Saya pikir Unpin
harus diutarakan dalam negatif "ini _not_ dibatasi oleh Pin
", sedangkan Pinned
harus diutarakan dalam positif "ini _is_ dibatasi oleh Pin
". Sebagian besar hanya untuk menghindari negatif ganda. Tapi itu detailnya; Saya suka gagasan tentang adanya dualitas. Mungkin: s/Unpin/TemporaryPin
, s/Pinned/PermanentPin
?
EDIT: Ya, saya melihat maksud Anda tentang Repin
menjadi efek samping dari Unpin
. Saya ingin mengkomunikasikan fakta bahwa Pin
"tidak penting" untuk jenis yang Unpin
, yang menurut saya Unpin
tidak bekerja dengan sangat baik. Oleh karena itu saran di atas TemporaryPin
.
@jonhoo Saya pikir alasan utama saya lebih suka yang sebaliknya adalah karena Pinned
mencegah suatu sifat diterapkan, jadi itu, dalam arti yang paling jelas bagi saya, negatif sebenarnya.
Sunting: bagaimana dengan:
Unpin -> Escape
Pinned -> NoEscape
Menarik .. Saya mencoba untuk melihat bagaimana itu cocok dengan dokumentasi. Sesuatu seperti:
Secara umum, ketika Anda diberi
Pin<P>
, itu disertai dengan jaminan bahwa targetP
tidak akan bergerak sampai target itu dijatuhkan. Pengecualian untuk ini adalah jika targetP
adalahEscape
. Jenis yang ditandai sebagaiEscape
janji bahwa mereka tetap valid meskipun dipindahkan (misalnya, tidak berisi referensi sendiri internal), dan karena itu diizinkan untuk "keluar" dariPin
.Escape
adalah sifat otomatis, jadi semua jenis yang seluruhnya terdiri dari jenis yangEscape
itu sendiri jugaEscape
. Pelaksana dapat memilih keluar dari ini pada malam hari menggunakanimpl !Escape for T {}
, atau dengan memasukkan penandaNoEscape
daristd::phantom
.
Tampaknya cukup baik, meskipun kaitannya dengan kata "melarikan diri" tampaknya agak renggang. Secara terpisah, tulisan di atas juga membuat saya menyadari bahwa Pin<P>
tidak _really_ menjamin bahwa target P
tidak akan dipindahkan (justru karena Unpin
). Alih-alih, ini menjamin bahwa _either_ tidak masalah jika target P
bergerak _atau_ P
target tidak akan dipindahkan. Tidak tahu bagaimana menggunakannya untuk menginformasikan pilihan nama yang lebih baik ... Tapi itu mungkin sesuatu yang harus dimasukkan ke dalam dokumen dengan satu atau lain cara.
Secara pribadi, saya juga sangat tidak menyukai Unpin
sebagai nama, mungkin karena paling sering dilihat sebagai impl !Unpin
yang berbunyi "not-un-pin" dan memerlukan beberapa siklus otak (saya Saya adalah model lama) untuk menyimpulkan bahwa itu berarti "oke, ini akan disematkan selamanya setelah disematkan pertama kali", jadi saya bahkan tidak dapat mengoptimalkan negatif ganda.
Secara umum, manusia cenderung lebih sulit berpikir negatif daripada positif (tanpa sumber langsung, tapi lihat karya Richard Hudson jika ragu).
Btw Repin
kedengarannya sangat menyenangkan bagiku.
Pin
sulit dijelaskan karena tidak selalu membuat nilai yang disematkan tidak dapat dipindahkan. Pinned
membingungkan karena sebenarnya tidak menyematkan apa pun. Ini hanya mencegah keluarnya Pin
.
Pin<P<T>>
dapat dijelaskan sebagai nilai yang disematkan: nilai yang disematkan tidak dapat bergerak kecuali nilainya adalah tipe yang dapat lolos dari pin.
Beberapa googling cepat tampaknya menunjukkan bahwa dalam gulat, ketika seseorang disematkan, melanggar pin disebut melarikan diri .
Saya juga menyukai istilah escape daripada Unpin
tapi saya akan memilih EscapePin
.
Banyak pemikiran bagus di sini!
Satu hal umum: Bagi saya sebagai non-native speaker Pin
dan Unpin
kebanyakan adalah kata kerja / tindakan. Meskipun Pin
masuk akal, karena objek disematkan ke satu lokasi memori pada satu waktu, saya tidak dapat melihat hal yang sama untuk Unpin
. Setelah saya menerima referensi Pin<&mut T>
, T akan selalu disematkan dalam arti lokasi memorinya stabil, entah itu Unpin
atau tidak. Tidak mungkin benar-benar melepas pin objek sebagai tindakan. Perbedaannya adalah bahwa jenis Unpin
tidak memerlukan jaminan penyematan untuk ditegakkan dalam interaksi lebih lanjut. Mereka tidak mengacu pada dirinya sendiri, dan alamat memorinya, misalnya, tidak dikirim ke objek lain dan disimpan di sana.
Saya setuju dengan @tikue bahwa alangkah baiknya memiliki karya yang mencolok untuk apa sebenarnya arti Unpin
, tetapi sulit untuk diukur. Bukankah hanya tipe-tipe itu yang bisa dipindah-pindah, dan itu juga bukan murni kurangnya referensi diri mereka? Mungkin itu adalah sesuatu tentang "tidak ada petunjuk di seluruh ruang memori yang tidak valid saat objek dipindahkan". Kemudian sesuatu seperti StableOnMoveAfterPin
, atau hanya StableMove
mungkin bisa menjadi pilihan, tapi itu juga terdengar tidak terlalu bagus.
Repin
bagi saya memiliki komplikasi yang sama seperti Unpin
, yang berarti bahwa satu hal pertama kali dilepas - yang menurut pemahaman saya tidak terjadi.
Karena sifat tersebut sebagian besar mendefinisikan apa yang terjadi setelah seseorang melihat Pin
dari jenis tersebut, saya menemukan hal-hal seperti PinNeutral
, atau PinInvariant
tidak terlalu buruk.
Mengenai Pin
vs Pinned
, saya pikir saya lebih suka Pinned
, karena itulah status penunjuk pada titik waktu ketika seseorang melihatnya.
@ Matthias247 Saya tidak berpikir bahwa Anda dijamin bahwa P::Target
alamat stabil jika P: Unpin
? Saya bisa saja salah tentang ini?
@Mutiarasni
Setelah saya menerima referensi Pin <& mut T>, T akan selalu disematkan dalam arti lokasi memorinya stabil, terlepas dari Lepas pin atau tidak.
Bisakah Anda menjelaskan apa yang Anda maksud dengan ini? Diberikan T: Unpin
Anda dapat menulis sebagai berikut:
let pin_t: Pin<&mut T> = ...
let mut other_t: T = ...
mem::replace(Pin::get_mut(pin_t), &mut other_t);
// Now the value originally behind pin_t is in other_t
@jonhoo Sebenarnya pertanyaan yang bagus. Alasan saya adalah bahwa masa depan dikotak, dan kemudian metode poll()
akan dipanggil pada alamat memori yang sama. Tapi itu jelas hanya berlaku untuk tugas tingkat atas / masa depan, dan lapisan perantara mungkin bergerak di masa depan ketika mereka Unpin
. Jadi sepertinya Anda benar.
Wrt bikeshedding terbaru:
Unpin
: bagaimana dengan MoveFromPin
? Jika saya masih tidak melewatkan beberapa kehalusan, saya pikir ini secara langsung menyatakan kemampuan apa yang sebenarnya diaktifkan oleh sifat tersebut: jika jenisnya berada dalam Pin
, Anda masih dapat memindahkannya.
Yang terpenting, ini semacam mengatakan hal yang sama seperti Unpin
, tetapi dibingkai sebagai pernyataan positif, jadi alih-alih negatif ganda !Unpin
kita sekarang memiliki !MoveFromPin
. Saya pikir saya menemukan itu lebih mudah untuk ditafsirkan, setidaknya ... itu eh, jenis yang tidak bisa Anda pindahkan dari pin.
(Ada beberapa ruang untuk variasi pada ide dasar: MoveOutOfPin
, MoveFromPinned
, MoveWhenPinned
, dan seterusnya.)
Pinned
: ini kemudian bisa menjadi NoMoveFromPin
, dan efeknya adalah membuat tipe !MoveFromPin
. Saya pikir itu tampaknya cukup mudah.
Pin
itu sendiri: yang satu ini tidak terhubung dengan dua lainnya dan juga tidak terlalu signifikan, tapi saya pikir mungkin ada ruang untuk sedikit perbaikan di sini juga.
Masalahnya adalah bahwa Pin<&mut T>
(misalnya) tidak berarti bahwa &mut
disematkan, itu berarti T
adalah (kebingungan yang menurut saya pernah saya lihat di setidaknya satu bukti komentar terbaru). Karena bagian Pin
bertindak sebagai semacam pengubah pada &mut
, saya pikir ada kasus bahwa akan lebih baik menyebutnya Pinning
.
Ada beberapa preseden tidak langsung untuk ini: jika kita ingin memodifikasi semantik overflow dari tipe integer untuk membungkus alih-alih panik, kita katakan Wrapping<i32>
daripada Wrap<i32>
.
Semua ini lebih panjang dari aslinya, tetapi mengingat betapa rumitnya beberapa alasan di sekitarnya, ini mungkin investasi yang berharga untuk kejelasan yang lebih besar.
Re: Repin
, Saya berharap ini menjadi sesuatu seperti
unsafe trait Repin {
unsafe fn repin(from: *mut Self, to: *mut Self);
}
yang dapat digunakan untuk mendukung !Unpin
jenis di dalam Vec
-seperti koleksi yang kadang-kadang bergerak isinya (ini bukan usulan untuk menambahkan sifat seperti sekarang atau pernah, hanya kesan pertama saya dari nama sifat).
Juga bikshedding nama:
Pin<P>
-> Pinned<P>
: nilai pointer P
poin akan disematkan di memori selama masa pakainya (sampai dijatuhkan).Unpin
-> Moveable
: nilai tidak perlu disematkan dan dapat dengan bebas dipindahkan.Pinned
(struct) -> Unmoveable
: harus Pinned
dan tidak dapat dipindahkan.Saya tidak berpikir Pin
atau Unpin
harus berubah, semua alternatif menambah verbositas tanpa kejelasan menurut saya, atau bahkan cukup menyesatkan. Kami juga sudah melakukan percakapan ini, memutuskan untuk menggunakan Pin
dan Unpin
, dan tidak ada argumen yang muncul di utas ini yang baru.
Namun, Pinned
telah ditambahkan sejak diskusi sebelumnya, dan menurut saya masuk akal untuk membuatnya menjadi PhantomPinned
untuk memperjelas jenis penanda hantu seperti PhantomData
.
Secara pribadi, saya juga sangat tidak menyukai Lepas Pin sebagai nama, mungkin karena ini paling sering dilihat sebagai impl! Lepas pin yang bertuliskan "not-un-pin" dan memerlukan beberapa siklus otak (saya model yang lebih tua) untuk menyimpulkan yang artinya "oke, ini akan disematkan selamanya setelah disematkan pertama kali", jadi saya bahkan tidak bisa mengoptimalkan negatif ganda.
Ini benar-benar kebalikan dari pengalaman saya, menerapkan secara manual !Unpin
berarti Anda menerapkan struktur referensi sendiri dengan tangan menggunakan kode yang tidak aman, kasus penggunaan yang sangat khusus. Sebaliknya, apa pun yang menyimpan struct yang berpotensi melepas pin di belakang pointer memiliki implikasi positif Unpin
. Semua implik dari Unpin
di std
adalah polaritas positif, misalnya.
@withoutboats dapatkah Anda memberikan tautan ke diskusi sebelumnya seputar nama-nama ini di mana argumen yang dikemukakan di sini telah diperdebatkan?
Ini satu utas, meskipun jelas juga dibahas di utas RFC dan masalah pelacakan https://internals.rust-lang.org/t/naming-pin-anchor-move/6864
(jenis di utas ini disebut jangkar dan pin sekarang disebut Pin<Box<T>>
dan Pin<&'a mut T>
)
Apakah Lepas pin seharusnya dibaca sebagai singkatan dari Unpinnable? Unpinnable karena Anda tidak dapat benar-benar menyimpan nilai yang disematkan, meskipun itu di dalam Pin. (Apakah itu pemahaman yang benar di pihak saya?)
Saya telah melihat-lihat beberapa dokumen dan utas komentar dan tidak melihat referensi apa pun ke Unpinnable secara khusus.
Lepas pin seharusnya tidak berarti apa pun. Satu hal yang menurut saya tidak jelas bagi banyak pengguna, tetapi benar, adalah bahwa panduan gaya pustaka standar lebih memilih kata kerja sebagai nama sifat, bukan kata sifat - karenanya Send
, bukan Sendable
. Ini belum diterapkan dengan konsistensi yang sempurna, tetapi ini adalah norma. Unpin
adalah seperti dalam "melepas pin", karena jenis ini dapat dilepas dari Pin
yang telah Anda sematkan.
Nama seperti Move
(bukan "dapat dipindahkan", ingat) kurang jelas daripada Unpin
karena menyiratkan bahwa ini berkaitan dengan kemampuan untuk memindahkannya sama sekali, daripada menghubungkan perilaku ke jenis pin. Anda dapat memindahkan jenis !Unpin
, karena Anda dapat memindahkan nilai Sized
di Rust.
Nama yang keseluruhan frase seperti yang saya lihat disarankan akan sangat tidak otomatis untuk std.
Ini mungkin tidak singkat, tapi itulah cara saya membacanya. Karena sifat adalah kata kerja, Anda harus mengubahnya secara manual menjadi kata sifat jika Anda ingin menggunakannya untuk mendeskripsikan sebuah tipe daripada operasi pada sebuah tipe; std::iter::Iterator
diimplementasikan oleh sesuatu yang bisa diulang, std::io::Seek
diimplementasikan oleh sesuatu yang bisa dicari, std::pin::Unpin
diimplementasikan oleh sesuatu yang tidak bisa diubah.
@withoutboats ada masalah khusus dengan beberapa nama lain yang tidak memiliki masalah kata kerja, misalnya Escape
atau EscapePin
? Memahami bahwa diskusi ini telah terjadi sebelumnya, tetapi mungkin lebih banyak perhatian tertuju pada ini sekarang, jadi saya tidak yakin ini adalah pengulangan yang sepenuhnya berlebihan ...
Satu hal yang menurut saya benar adalah sangat disayangkan bahwa Pin
dan Unpin
dapat dibaca sebagai pasangan (beberapa tipe adalah "pin" dan beberapa "lepas pin"), ketika Pin
seharusnya menjadi kata benda , bukan kata kerja . Fakta bahwa Pin
bukanlah sifat mudah-mudahan dapat menjelaskan banyak hal. Argumen yang mendukung Pinning
masuk akal, tetapi di sini kita menghadapi masalah panjang nama. Terutama karena penerima metode harus mengulang self
dua kali, kita akan mendapatkan banyak karakter: self: Pinning<&mut Self>
. Tidak yakin bahwa Pinning<P>
adalah keseluruhan empat karakter yang bernilai lebih dari Pin<P>
.
@tikue , terminologi "melarikan diri" jauh lebih kelebihan beban daripada menjepit, menurut saya, bertentangan dengan konsep seperti analisis melarikan diri.
Kami juga sudah melakukan percakapan ini, mengambil keputusan untuk menggunakan Pin dan Lepas Pin, dan tidak ada argumen yang muncul di utas ini yang baru.
Ini membuat saya salah paham - apakah laporan pengalaman komunitas tidak diinginkan? Saya pribadi tidak melihat masalah yang jelas dengan Unpin
hingga beberapa komentar lain di utas ini, serta penjajaran dengan negatif ganda Pinned
.
Rust tidak melakukan analisis melarikan diri, jadi saya tidak yakin saya melihatnya sebagai masalah nyata.
: bell: Ini sekarang memasuki periode komentar terakhirnya , sesuai ulasan di atas . :lonceng:
Saya masih ingin melihat peningkatan dokumentasi seperti yang diuraikan di https://github.com/rust-lang/rust/issues/55766#issuecomment-438316891 sebelum ini hadir :)
Saya telah membuka https://github.com/rust-lang/rust/pull/55992 untuk menambahkan dokumentasi yang disarankan di atas dan mengganti nama Pinned
menjadi PhantomPinned
.
Saya pikir Pinned
(dan PhantomPinned
) mendorong konsep nilai "yang disematkan" sebagai nilai yang tidak dapat keluar dari Pin
, yang berarti banyak nilai dalam Pin
(yang tipenya impl Unpin
) tidak "disematkan"!
Sepertinya membingungkan. Saya merasa lebih mudah untuk mengkonseptualisasikan semua nilai dalam Pin
seperti yang disematkan saat berada di Pin
, dan apakah disematkan permanen atau tidak adalah apa yang sebelumnya dinamai Pinned
kontrol. Nama yang terpisah dari Pin*
akan mencegah penggabungan dua konsep yang berbeda.
PhantomNotUnpin
: P
Secara pribadi, saya juga sangat tidak menyukai Lepas Pin sebagai nama, mungkin karena ini paling sering dilihat sebagai impl! Lepas pin yang bertuliskan "not-un-pin" dan memerlukan beberapa siklus otak
Terima kasih! Saya juga pernah terganggu oleh Unpin
selama beberapa waktu bot tidak bisa pin-point (heh) kenapa. Sekarang saya pikir saya mengerti: Ini adalah negasi ganda.
Ini benar-benar kebalikan dari pengalaman saya, menerapkan secara manual! Lepas pin berarti Anda menerapkan struktur referensi mandiri dengan tangan menggunakan kode yang tidak aman, kasus penggunaan yang sangat khusus. Sebaliknya, apa pun yang menyimpan struct yang berpotensi melepas pin di belakang penunjuk memiliki implikasi positif dari Lepas Pin. Semua implik dari Unpin di std adalah polaritas positif, misalnya.
Ini bukan hanya tentang implementasi, juga tentang diskusi. impl !Sync
cukup langka (tidak sendirian karena tidak stabil), tetapi membicarakan tentang jenis Sync
dan !Sync
cukup umum. Demikian pula, !Unpin
muncul cukup banyak dalam diskusi tentang fitur ini, setidaknya yang saya miliki.
Saya, juga, lebih suka sesuatu yang secara positif mengungkapkan properti ( MoveFromPin
atau lebih). Saya tidak sepenuhnya yakin dengan ergonomi, karena tidak seperti Pin
seseorang seharusnya tidak perlu terlalu sering menulis sifat ini terikat.
Rust tidak melakukan analisis melarikan diri, jadi saya tidak yakin saya melihatnya sebagai masalah nyata.
LLVM melakukannya, jadi analisis pelarian masih cukup relevan dengan Rust.
Cara mengatasinya adalah dengan memilih kata-kata yang membalikkan arti dari Pin
/ Unpin
. Misalnya, ganti nama Unpin
menjadi Relocate
. Kemudian !Unpin
menjadi !Relocate
. Itu jauh lebih masuk akal bagi saya - saya membacanya sebagai "Oh, objek jenis ini tidak dapat dipindahkan". Pesaing lainnya adalah Movable
.
Saya tidak yakin apa kata kebalikannya yang bisa menggantikan Pin
, atau bahkan jika kita perlu. Tapi saya pasti bisa membayangkan dokumen mengatakan sesuatu seperti ini:
Objek yang disematkan dapat dimodifikasi secara langsung melalui
DerefMut
jika dan hanya jika objek tersebut dapat dipindahkan ke memori.Relocate
adalah sifat otomatis - ini ditambahkan secara default. Tetapi jika akan ada petunjuk langsung ke memori yang menyimpan nilai Anda, pilih keluar dariRelocate
dengan menambahkanimpl !Relocate
pada tipe Anda.
impl<T: Relocate> DerefMut for Pin<T> { ... }
Bagi saya ini jauh lebih masuk akal daripada Unpin
.
Saya telah membuka # 55992 untuk menambahkan dokumentasi yang disarankan di atas
Ini hanya menambahkan sebagian dari apa yang disarankan di https://github.com/rust-lang/rust/issues/55766#issuecomment -438316891.
Saya suka saran MoveFromPin
. Relocate
juga bagus, tapi mungkin tidak terkait dengan Pin
cukup. Seseorang dapat lagi memahaminya sebagai tipe yang tidak dapat dipindahkan (yang sebenarnya bukan). RelocateFromPin
lagi-lagi akan bagus.
Escape
ing adalah hal yang juga diasosiasikan di Swift dengan closure, dan apakah mereka dipanggil di dalam atau di luar rantai panggilan saat ini. Kedengarannya menyesatkan.
Saya tidak melihat ada masalah dengan nama yang lebih panjang selama itu membantu kejelasan.
FWIW Saya ingin memberikan suara juga untuk mengganti nama Unpin
menjadi sesuatu yang "positif" seperti Relocate
atau MoveFromPin
(atau bahkan lebih panjang lebar tetapi mungkin sedikit lebih akurat MayMoveFromPin
).
Saya setuju bahwa negatif ganda !Unpin
atau hanya Unpin
telah membingungkan saya secara historis, dan sesuatu yang dibingkai dalam positif "ini dapat bergerak meskipun berada di dalam Pin
" Saya pikir akan membantu mengurangi kebingungan!
FWIW Saya awalnya memikirkan hal yang sama tentang Unpin
, tetapi ketika saya benar-benar menggunakannya IMO itu masuk akal - itu sebenarnya bukan negasi ganda, karena operasi yang Anda cari adalah kemampuan untuk Unpin
(mengambil sesuatu yang masuk dan keluar Pin
bebas) bukan kemampuan untuk menyimpan sesuatu di dalam pin. Ini sama dengan MoveFromPin
, hanya kata-katanya berbeda. Saya lebih suka nama yang tidak membuat orang berpikir itu "bukan Pin
" atau semacamnya, tapi IMO MoveFromPin
dan lainnya terlalu bertele-tele. UndoPin
? ( FreePin
untuk kerumunan haskell?)
Saya masih berpikir !Unpin
terbaca aneh - Saya telah melatih suara hati saya untuk memperlakukannya lebih seperti "Jangan lepas pin ini!" daripada yang biasa "Tidak mengimplementasikan Unpin
", tetapi ini membutuhkan usaha.
Bagaimana dengan !Pluck
?
@bayu_joo
bukannya "Tidak menerapkan Lepas pin" yang biasa
Saya menyebutkan ini dalam komentar saya sebelumnya, tetapi saya pikir ini sebenarnya cara yang bagus untuk mengatakannya: Unpin
adalah operasi mengambil masuk dan keluar dari Pin<C<_>>
. Hal-hal yang tidak menerapkan Unpin
tidak menyediakan kemampuan itu.
@cramertj Saya cukup suka UndoPin
@cramertj Saya setuju bahwa saya bukan penggemar berat alternatif yang diusulkan sejauh ini, tetapi saya pribadi akan menyukai kata-kata MoveFromPin
lebih dari Unpin
. Ini adalah poin yang bagus bahwa ini bukan double-negative, tapi saat membacanya (sebagai seseorang yang belum bekerja sama sekali) itu membuat saya tersandung bahwa itu adalah double negative. Saya terus mencoba membaca awalan "Un" sebagai negatif ...
Saya ingin tahu, tetapi @cramertj atau yang lain apakah Anda merasa ada pegangan yang baik tentang berapa banyak Unpin
terikat secara ergonomis? Apakah ini sangat langka? Apakah ini sangat umum? Cukup umum untuk menjadi sakit jika itu adalah rasa sakit untuk mengetik?
Untuk nama yang pendek dan manis, saya pribadi menyukai ide Relocate
, tetapi untuk ide yang lebih panjang-dan-lebih-kata-tapi-ok-karena-Anda-tidak-mengetik-sebanyak-saya suka MoveFromPin
. Saya pribadi merasa bahwa terlepas dari ergonomi pengetikan nama keduanya lebih baik dari Unpin
Saya ingin tahu, tetapi @cramertj atau yang lainnya apakah Anda merasa ada pegangan yang baik tentang seberapa banyak ikatan Lepas pin muncul secara ergonomis? Apakah ini sangat langka? Apakah ini sangat umum? Cukup umum untuk menjadi sakit jika itu adalah rasa sakit untuk mengetik?
Menurut pengalaman saya, ada dua kasus di mana Unpin
sebenarnya muncul di kode pengguna:
Unpin
bounds muncul karena Anda benar-benar perlu untuk memindahkan masa depan saat Anda melakukan polling (misalnya hal-hal seperti beberapa API tertentu).Unpin
, karena tidak masalah jika jenis lain itu Unpin
karena Anda tidak pernah sematkan proyek ke mereka.Contoh kasus kedua adalah jika Anda memiliki beberapa jenis buffer yang Anda gunakan secara umum (misalnya T: AsRef<[u8]>
). Anda tidak perlu menyematkannya untuk mengeluarkan potongannya, jadi Anda tidak peduli apakah itu mengimplementasikan Unpin
atau tidak, jadi Anda hanya ingin mengatakan tipe Anda mengimplementasikan Unpin
tanpa syarat sehingga Anda dapat mengimplementasikan Future
mengabaikan pemasangan pin.
Unpin
cukup umum untuk dilihat sebagai ikatan-- select!
, StreamExt::next
dan kombinator lainnya semuanya membutuhkan tipe yang mereka operasikan menjadi Unpin
.
@withoutboats Saya ingin tahu tentang poin 2 Anda di sana; apakah menurut kami impl Unpin
adalah sesuatu yang harus diingat orang untuk sering diterapkan? Mirip dengan bagaimana penulis perpustakaan saat ini sering lupa #[derive(Debug)]
atau impl std::error::Error
untuk jenis kustom mereka, yang membuatnya lebih sulit untuk menggunakan perpustakaan tersebut?
Lepas pin adalah sifat otomatis. Satu-satunya saat Anda akan memiliki jenis yang tidak menerapkan pelepasan pin adalah saat jenis tersebut secara eksplisit menyisih. (Atau berisi bidang yang memilih untuk tidak melepaskan pin).
Saya mengerti bahwa itulah masalahnya. Dan saya berasumsi bahwa sejauh ini itu akan menjadi kasus yang paling umum, itulah sebabnya saya terkejut bahwa @withoutboats bahkan menyebutkan poin kedua. Ini menunjukkan kepada saya bahwa ini mungkin lebih umum daripada yang saya kira (meskipun mungkin hanya ketika menerapkan masa depan Anda sendiri), jadi saya ingin tahu tentang frekuensi kasus penggunaan semacam itu :)
@alexcrichton Saya ingin tahu, tetapi @cramertj atau yang lainnya apakah Anda merasa ada pegangan yang baik tentang seberapa banyak ikatan Unpin yang ergonomis muncul? Apakah ini sangat langka? Apakah ini sangat umum? Cukup umum untuk menjadi sakit jika itu adalah rasa sakit untuk mengetik?
Saya telah menulis posting panjang tentang pengalaman saya mem-porting kode saya ke Futures 0.3 (yang menggunakan Pin
). Anda tidak perlu membacanya, ringkasannya adalah:
Sebagian besar waktu Anda tidak perlu khawatir tentang Unpin
sama sekali, karena Unpin
diterapkan secara otomatis untuk hampir semua jenis.
Jadi satu-satunya saat Anda perlu khawatir tentang Unpin
adalah:
Anda memiliki tipe yang umum dibandingkan tipe lainnya (mis. struct Foo<A>
).
Dan Anda ingin menerapkan API penyematan (misalnya Future
/ Stream
/ Signal
) untuk jenis itu.
Dalam hal ini Anda perlu menggunakan ini:
impl<A> Unpin for Foo<A> where A: Unpin {}
Atau ini:
impl<A> Unpin for Foo<A> {}
impl<A> Future for Foo<A> where A: Unpin { ... }
Biasanya itulah satu-satunya situasi di mana Unpin
dibutuhkan. Seperti yang Anda lihat, itu biasanya berarti perlu menggunakan Unpin
~ 2 kali per jenis.
Dalam kasus non-kombinator, atau dalam kasus tipe yang tidak menerapkan Future
/ Stream
/ Signal
, Anda tidak perlu menggunakan Unpin
sama sekali.
Jadi saya akan mengatakan bahwa Unpin
muncul sangat jarang, dan itu hanya benar-benar muncul dalam situasi membuat Future
/ Stream
/ Signal
combinators.
Jadi saya sangat menyukai nama seperti MoveFromPin
. Memasang pin adalah fitur khusus yang tidak perlu ditangani oleh kebanyakan orang, jadi kita tidak boleh terlalu mengoptimalkan panjang nama.
Saya pikir manfaat kognitif (menghindari negasi ganda) jauh lebih penting daripada menyimpan beberapa karakter dalam situasi yang jarang terjadi.
Terutama karena pemasangan pin sudah cukup sulit untuk dipahami! Jadi jangan membuatnya lebih sulit.
@jonhoo, apakah menurut kami
impl Unpin
adalah sesuatu yang harus diingat orang untuk sering diimplementasikan? Mirip dengan bagaimana penulis perpustakaan saat ini sering lupa#[derive(Debug)]
atauimpl std::error::Error
untuk jenis kustom mereka, yang membuatnya lebih sulit untuk menggunakan perpustakaan tersebut?
Saya rasa tidak mungkin lupa impl Unpin
, karena jika penulis lupa, mereka akan mendapatkan kesalahan kompilator, yang mencegah peti mereka dipublikasikan. Jadi sama sekali tidak seperti #[derive(Debug)]
.
Situasi yang dibicarakan @withoutboats hanya untuk orang yang menerapkan Future
/ Stream
/ Signal
combinators, itu tidak mempengaruhi orang lain (khususnya, itu tidak mempengaruhi pengguna hilir perpustakaan).
(Saya pikir negasi ganda adalah bagian darinya, dan mungkin lebih-negasi-daripada-benar-benar-diperlukan, tapi saya merasa itu bukan keseluruhan cerita (mencoba introspeksi di sini) ... "Lepas pin" itu sedikit , metafora? Tidak langsung? Masuk akal ketika dijelaskan, dan karena "menjepit" itu sendiri sudah menjadi metafora, tidak jelas mengapa otak saya akan bermasalah dengan mengambil lebih jauh dari metafora itu, tetapi, meskipun demikian, otak saya menemukan arti dari "lepas pin" karena alasan tertentu tidak jelas dan sulit dipasang dengan kuat.)
Saya kira Anda benar @cramertj , sebenarnya ini bukan negasi ganda dalam arti biasa, tetapi seperti @alexcrichton dan @glaebhoerl saya terus tersandung olehnya. "UN" sebagai awalan memiliki sangat perasaan negasi-y untuk itu ( "tidak aman", "diimplementasikan" dan seterusnya adalah bagaimana saya biasanya menemukan awalan itu), dan itu meniadakan menjepit di sini, jika hanya sebagai kata kerja.
@withoutboats Saya ingin tahu tentang poin 2 Anda di sana; apakah menurut kami impl Unpin adalah sesuatu yang harus diingat orang untuk sering diterapkan? Mirip dengan bagaimana penulis perpustakaan saat ini sering lupa untuk # [derive (Debug)] atau impl std :: error :: Error untuk tipe khusus mereka, yang membuat lebih sulit untuk menggunakan perpustakaan tersebut?
Benar-benar tidak! Jika pengguna secara manual mengimplementasikan Future
yang bersifat generik di masa mendatang, mereka mungkin ingin dapat mengubah status dalam implementasi mendatang tanpa menulis kode yang tidak aman - yaitu, memperlakukan Pin<&mut Self>
sebagai &mut self
. Mereka akan mendapatkan kesalahan yang menunjukkan bahwa MyFuture<T: AsRef<[u8]>>
tidak mengimplementasikan Unpin
. Solusi terbaik untuk masalah ini adalah dengan mengimplementasikan Unpin
. Tetapi satu-satunya dampak ini adalah pada pengguna yang mencoba mengimplementasikan Future
, dan tidak mungkin bagi mereka untuk melupakannya karena kode mereka tidak dapat dikompilasi.
Situasi yang dibicarakan oleh @withoutboats hanya untuk orang-orang yang mengimplementasikan kombinator Future / Stream / Signal, itu tidak mempengaruhi orang lain (khususnya, ini tidak mempengaruhi pengguna perpustakaan hilir).
Saya secara khusus berbicara tentang masa depan manual generik non-kombinator , yang seharusnya hanya memiliki implikasi selimut Unpin
bahkan jika obat generik mereka bukan Unpin
.
Saya membuat diagram alur untuk pertanyaan "Apa yang harus saya lakukan tentang pemasangan pin saat saya menerapkan masa depan / aliran manual?"
Untuk lebih banyak bersepeda, hari ini saat makan siang saya mendapatkan LeavePin
. Ini membawa nada yang sama seperti escape
tanpa semantik menyesatkan yang tersirat.
Apakah ada interaksi yang menarik antara impl specialist dan pin, seperti halnya dengan masa pakai?
Substring "spesialis" muncul saat melacak diskusi masalah , tetapi tidak konklusif. Pin RFC atau spesialisasi RFC harus secara eksplisit menyebutkan interaksi ini, baik sebagai "Sudah diverifikasi untuk menjadi OK" atau "penelitian lebih lanjut diperlukan untuk menilai aman".
@vi tidak hanya tidak ada interaksi yang sehat, juga tidak mungkin ada interaksi yang sehat. API pin didefinisikan secara ketat di pustaka standar dan tidak melibatkan fitur bahasa baru, pengguna dapat mendefinisikannya dengan mudah di pustaka pihak ketiga. Jika fitur bahasa tidak benar karena kode perpustakaan ini ada, itu adalah periode yang tidak benar, karena setiap pengguna dapat menulis kode perpustakaan ini hari ini dan itu akan dikompilasi dengan baik.
Jika fitur bahasa tidak benar karena kode perpustakaan ini ada, itu adalah periode tidak sehat, karena setiap pengguna dapat menulis kode perpustakaan ini hari ini dan itu akan dikompilasi dengan baik.
Bukan berarti itu harus ada hubungannya dengan pin
... tapi menurut saya tidak sesederhana itu.
Jika fitur perpustakaan, saat menggunakan unsafe
, menggunakan konstruksi bahasa yang perilakunya masih belum ditentukan (misalnya &packed.field as *const _
, atau membuat berbagai asumsi tentang ABI) maka jika perubahan bahasa tambahan membatalkan asumsi dari perpustakaan-perpustakaan itu maka saya pikir itu adalah perpustakaan yang tidak sehat dan bukan bahasa yang berubah. Di sisi lain, jika perubahan bahasa membuat perilaku yang ditentukan menjadi tidak sehat maka itu adalah kesalahan dari bahasa yang berubah. Karenanya, mengompilasi dengan baik bukanlah kondisi yang cukup untuk kesehatan perpustakaan dalam menghadapi perubahan bahasa dan tidak aman.
1 untuk MoveFromPin atau serupa
Jika Anda mengajukan pertanyaan "Kapan saya harus membatalkan implementasi Lepas pin untuk tipe saya sendiri?", Jawabannya akan jauh lebih jelas jika Anda malah bertanya "Kapan saya harus membatalkan penerapan MoveFromPin untuk tipe saya sendiri?"
Sama dengan "Haruskah saya menambahkan Lepas pin sebagai sifat yang terikat di sini?" vs "haruskah saya menambahkan MoveFromPin sebagai ciri yang terikat di sini?"
Tidak memasang pin! Pindahkan
Maaf jika ini disebutkan di suatu tempat, tetapi saya hanya membaca sekilas sejumlah besar diskusi yang ada seputar Pin di sini, dalam masalah implementasi, dan dalam masalah RFC.
Apakah karat akan pernah ada! Saya pasti dapat melihat kasus penggunaan untuk hal semacam itu (sih, saya datang mencari tentang Pin karena saya sedang mencari cara untuk tidak menembak diri saya sendiri dengan tipe yang tidak dapat dipindahkan). Jika jawabannya ya, bagaimana cara berinteraksi dengan Pin? Akankah keberadaan Pin membuatnya lebih sulit daripada sebelumnya!
Periode komentar terakhir, dengan disposisi untuk digabungkan , sesuai ulasan di atas , sekarang selesai .
Seharusnya ada kekhawatiran yang belum terselesaikan seputar penamaan Lepas Pin.
Seperti yang ditunjukkan oleh @RalfJung , # 55992 juga hanya menambahkan sejumlah kecil dokumentasi tambahan yang diminta di https://github.com/rust-lang/rust/issues/55766#issuecomment -438316891 dan lainnya. Tidak tahu bahwa itu alasan untuk belum bergabung.
Mengapa metode drop () saya sekarang mengambil & mut self lagi, bukan Pin.
Nah, drop () sudah tua - itu ada sejak Rust 1.0 - jadi kita tidak bisa mengubahnya. Kami ingin membuatnya mengambil Pin <& mut Self>, dan kemudian jenis Lepas Pin bisa mendapatkan & mut seperti yang mereka lakukan sekarang, tetapi itu adalah perubahan yang tidak kompatibel ke belakang.
Saya bertanya-tanya apakah mungkin menerapkan perubahan ini dengan cara yang kompatibel ke belakang. AIUI sampai kita menambahkan Unpin
(dan orang dapat menentukan !Unpin
) semua tipe mengimplementasikan Unpin
. Jadi kita bisa menambahkan ciri:
trait DropPinned {
fn drop(Pin<&mut> self);
}
dan kemudian mengimplikasikan sifat ini untuk semua tipe Unpin
, yang sampai orang dapat menyisih adalah semua tipe. Sesuatu seperti:
impl<T> PinDrop for T where T:Unpin + Drop {
fn drop(Pin<&mut T> self) {
Drop::drop(self.get_mut());
}
}
Kemudian kita akan memiliki panggilan insert compiler ke DropPinned::drop
daripada Drop::drop
. Pada dasarnya ciri DropPinned
menjadi lang-item daripada Drop
. AFAICS ini akan kompatibel ke belakang jika dan hanya jika mekanisme ini diperkenalkan pada saat yang sama dengan Unpin
.
Seharusnya ada kekhawatiran yang belum terselesaikan seputar penamaan Lepas Pin.
@tikue Tidak ada anggota tim libs yang mengajukan masalah dengan rfcbot sebelum atau selama FCP, dan saya tidak yakin argumen apa pun tentang Unpin
baru atau baru untuk utas ini, jadi biasanya proses kami akan mempertimbangkan arus penamaan untuk diselesaikan. Tentunya jika seseorang memiliki kekhawatiran, mereka harus angkat bicara, tetapi inti dari kotak centang tim yang diikuti oleh FCP adalah untuk memastikan bahwa semua orang merasa nyaman menstabilkan API seperti yang diusulkan.
@cramertj Saya agak bingung. Beberapa orang telah angkat bicara. Ketika saya meminta referensi tentang di mana lagi argumen tentang penamaan Unpin
telah dimunculkan dan diselesaikan, saya diarahkan ke https://internals.rust-lang.org/t/naming-pin-anchor- move / 6864 , yang sejauh yang saya bisa lihat juga ada orang yang mengeluh tentang penamaan Unpin
, dan tidak ada argumen tandingan yang nyata. RFC asli di https://github.com/rust-lang/rfcs/pull/2349 juga tidak memiliki banyak alasan mengapa alternatif yang diusulkan untuk Unpin
lebih buruk. Bahkan di utas ini, tampaknya satu-satunya argumen balasan yang benar-benar muncul adalah "itu lebih pendek" dan "secara teknis benar". Apakah Anda bisa menunjuk ke diskusi konkret di mana nama-nama alternatif yang lebih mudah dipahami (seperti MoveFromPin
) dibahas dan ditolak?
Saya telah menjelaskan di komentar sebelumnya mengapa saya percaya perspektif baru telah dikemukakan di utas ini. Saya telah mengikuti diskusi API pin cukup dekat sejak awal dan tidak ingat pernah melihat masalah negatif ganda muncul sebelum utas ini.
@tikue Saya telah mengungkit dan melihat masalah negatif ganda Unpin
telah diangkat sebagai masalah berkali-kali dan telah diselesaikan secara konsisten demi Unpin
. Seperti yang saya katakan sebelumnya, jika seseorang di tim libs ingin mendaftarkan keberatan (terlambat) maka saya akan menjawabnya dengan baik, tetapi mereka semua menandatangani proposal stabilisasi di atas yang jelas tidak menyertakan penamaan Unpin
sebagai pertanyaan yang belum terselesaikan: kita telah mendiskusikan alternatifnya, dan FCP ini adalah proses untuk memutuskan bahwa kita siap untuk menstabilkan keputusan yang telah dibuat, termasuk nama Unpin
.
@cramertj Bisakah Anda memberikan tautan ke tempat diskusi itu terjadi? Saya tidak meragukan Anda, hanya ingin melihat argumen yang mendukung Unpin
, karena saya tidak percaya argumen tersebut diberikan di sini. Seperti yang disebutkan, referensi yang saya berikan sejauh ini tidak memberikan resolusi apa pun tentang penamaan Unpin
.
@cramertj +1 untuk @jonhoo 's ask. Jika ada diskusi yang dilakukan tim libs di antara mereka sendiri yang tidak terdaftar di jalur resmi, saya pikir tujuan utama dari diskusi tersebut harus diulangi di sini. Saya pikir bahkan ada aturan resmi bahwa keputusan RFC hanya dapat dibuat berdasarkan argumen yang diketahui publik?
Saya pikir bahkan ada aturan resmi bahwa keputusan RFC hanya dapat dibuat berdasarkan argumen yang diketahui publik?
Yup, itu benar-- dan sebagai catatan, saya bukan anggota tim libs dan karena itu belum hadir untuk diskusi khusus tim libs. Melihat melalui utas RFC dan masalah pelacakan Pin
, ada banyak waktu di mana nama Unpin
diangkat. Saya tidak melihat ada orang yang secara spesifik mengucapkan kata-kata "negatif ganda", tetapi saya pasti ingat " !Unpin
adalah negatif ganda" yang disebutkan sebelumnya, selain aturan API umum tentang sifat penamaan untuk apa yang Anda bisa lakukan dengan mereka, daripada apa yang tidak bisa Anda lakukan (seperti yang saya tunjukkan di atas, saya pikir Unpin
sebenarnya mengikuti kedua aturan ini, meskipun menyadari hal itu membutuhkan mendengar "Lepas pin" sebagai kata kerja daripada mendengarnya sebagai kata sifat "not-pin", yang tidak intuitif bagi orang).
@wmanley Itu tidak berhasil, misal impl<T> Drop for Vec<T>
akan rusak karena kita tidak memiliki Vec<T>: Unpin
. Juga, proposal sepanjang garis ini telah dibuat sebelumnya, bahkan di utas ini . Harap baca diskusi sebelum menjawab. Saya tahu itu meminta cukup banyak, tetapi itu adalah satu-satunya cara untuk menghindari masalah yang sama dijelaskan berulang kali.
Saya pikir bahkan ada aturan resmi bahwa keputusan RFC hanya dapat dibuat berdasarkan argumen yang diketahui publik?
Ini secara informal dikenal sebagai aturan "tidak ada alasan baru" .
Tidak yakin di mana harus memposting ini, tetapi bisakah seseorang melihat di https://github.com/rust-lang/rust/issues/56256 ?
Terkait dengan # 56256, impl<T> From<Box<T>> for Pin<Box<T>>
tidak terdaftar di OP sebagai distabilkan, tetapi secara implisit akan menjadi stabil setelah Pin
melakukannya. Apakah ada implementasi sifat lain yang tidak sepele dan harus dilihat untuk stabilisasi? (memindai dokumen, semua dokumen lainnya tampaknya merupakan implementasi pendelegasian yang sepele untuk penunjuk yang dibungkus kepada saya).
Kami membicarakan Masalah ini di tim libs hari ini. Pembahasan minggu-minggu terakhir menunjukkan bahwa masih ada beberapa hal yang harus dibenahi terlebih dahulu, khususnya mengenai penamaan Unpin
.
Oleh karena itu, kami tidak akan bergerak maju dengan menstabilkan ini untuk saat ini (meskipun demikian FCP).
Akan sangat dihargai jika seseorang dapat mengumpulkan ide-ide di utas ini dan menyiapkan proposal mandiri untuk meningkatkan situasi penamaan.
@Bayu_joo
Akan sangat dihargai jika seseorang dapat mengumpulkan ide-ide di utas ini dan menyiapkan proposal mandiri untuk meningkatkan situasi penamaan.
Apakah ini berarti bahwa tim libs tidak ingin menstabilkan API saat ini sebagaimana adanya? Saya pribadi tidak berpikir ada orang yang datang dengan nama yang lebih baik daripada kumpulan nama saat ini seperti yang diterapkan, jadi saya tidak dapat membuat proposal seperti itu, tetapi saya sangat peduli untuk melihat API ini distabilkan, jadi jika seseorang dari tim libs memiliki nama yang mereka sukai maka: shipit:
Menyematkan ini dengan sekelompok rekan kerja dan @anp menyarankan DePin
, yang sebenarnya cukup saya sukai, karena ini menghilangkan konotasi "bukan pin" dari Unpin
dan menekankan bahwa ini berbicara tentang sebuah tipe yang dapat menjadi Pin
'd.
@Kimundi dapatkah Anda atau seseorang dari tim libs mendaftarkan "perhatian fcp" secara eksplisit untuk mengeluarkan ini dari FCP dan menjelaskan lebih jelas apa yang dimaksud dengan "beberapa hal yang harus ditangani"?
@rfcbot perhatian penamaan-dari-Lepas pin
Saya tidak yakin apakah ini benar-benar berfungsi setelah FCP dimasukkan, tetapi saya ingin menempatkan masalah pemblokiran formal pada penamaan ciri Unpin
. Sifat Unpin
kelihatannya sangat penting untuk API, dan "negatif ganda" (seperti yang dibahas di atas) membuat saya berputar setiap kali saya membacanya.
Ada banyak komentar tentang berbagai nama, tapi sayangnya saya belum terlalu senang dengan apa pun. "Favorit" saya masih di sepanjang baris MoveFromPin
atau Relocate
(tuhan saya ada begitu banyak komentar di sini, saya tidak tahu bagaimana mengulasnya).
Saya pribadi baik-baik saja dengan penamaan Pin
itu sendiri serta Pinned
untuk ZST yang tidak menerapkan sifat Unpin
.
Saya sepenuhnya setuju dengan @alexcrichton bahwa penamaan Unpin
adalah poin utama perdebatan di sini. Saya tidak berpikir ada masalah teknis sejauh yang saya bisa lihat tentang fitur yang diusulkan itu sendiri (meskipun ada _are_ banyak komentar, jadi bisa saja melewatkan sesuatu).
Saya masih berpikir Pinned
adalah nama yang aneh untuk ZST, karena sesuatu yang mengandung Pinned
tidak benar-benar disematkan .. Hanya saja bukan Unpin
. PhantomPinned
(karena telah diubah namanya menjadi # 55992) memiliki masalah yang sama karena mengacu pada Pin
, ketika ZST _really_ sekitar Unpin
.
Saya juga masih berpikir dokumen membutuhkan lebih banyak pekerjaan mengingat kehalusan fitur ini, tetapi itu mungkin tidak dihitung sebagai pemblokir.
Juga, @Kimundi , saya senang melihat tim libs bersedia memberikan lebih banyak waktu untuk menyelesaikannya. Ini mungkin tampak seperti bikeshedding yang tidak perlu, tetapi saya (dan orang lain juga tampaknya) merasa cukup penting untuk meningkatkan kemampuan mengajar fitur ini :)
Setuju dengan @jonhoo tentang Pinned
masih merasa aneh, dan PhantomPinned
tidak menjadi lebih baik (tidak disematkan dengan cara apa pun, bahkan tidak dengan cara hantu). Saya pikir jika kita menemukan nama yang bagus untuk Unpin
, maka Pinned
secara alami akan berganti nama menjadi Not{NewNameForUnpin}
.
Saya benar - PhantomPinned
- jenis ini hampir tidak akan PhantomNotUnpin
/ PhantomNotDePin
/ PhantomNotMoveFromPin
/ dll. Tidak akan membuatnya lebih atau kurang umum atau membuat API lebih atau kurang jelas bagi pengguna yang sudah cukup nyaman dengan API untuk menghasilkan penggunaan yang sah untuk PhantomPinned
.
Ide singkatnya: trait Move
dan ZST Anchor
.
Setiap jenis bisa Move
, kecuali jika mengandung Anchor
yang membuatnya menempel pada Pin
.
Saya suka bagaimana Anchor: !Move
secara intuitif sangat masuk akal.
Saya tidak menyarankan agar kita menghabiskan waktu secara khusus untuk PhantomPinned
, tetapi menurut saya upaya yang rendah untuk tetap berpikiran terbuka, karena mungkin saja apa pun yang dipasang seharga Unpin
akan berhasil juga untuk PhantomPinned
.
Kami telah menutupi Move
dan menjelaskan mengapa itu tidak pantas berkali-kali. Semua jenis dapat dipindahkan hingga disematkan. Demikian pula, Anchor
sebelumnya telah disarankan tetapi tidak jelas sama sekali dari nama yang digunakan untuk menyisih dari Unpin
ing / Move
ing.
@stjepang Saya pikir Move
telah dibuang beberapa waktu yang lalu karena sesuatu yang menjadi !Unpin
sebenarnya tidak mencegahnya untuk dipindahkan. Hanya jika sebuah tipe di bawah Pin
, dan bukan Unpin
, Anda "diwajibkan secara kontrak" untuk tidak memindahkannya.
Dalam komentar asli @withoutboats mengatakan:
Pin
wrapper memodifikasi pointer untuk "menyematkan" memori yang dirujuknya pada tempatnya
Saya pikir itu aneh bahwa jenis menerapkan Unpin
karena nilai tidak "disematkan" - memori. Namun, masuk akal untuk membicarakan nilai-nilai yang "aman untuk dipindahkan". Bagaimana jika kita mengganti nama Unpin
menjadi MoveSafe
?
Pertimbangkan sifat UnwindSafe
. Ini hanya sifat penanda "petunjuk" dan aman untuk diterapkan. Jika Anda membiarkan nilai !UnwindSafe
melewati batas catch_unwind
(dengan AssertUnwindSafe
), Anda akan "mematahkan" dengan membatalkan invariannya.
Demikian pula, jika Anda memiliki nilai !Unpin
/ !MoveSafe
, nilai tersebut masih dapat dipindahkan (tentu saja), tetapi Anda akan "merusak" dengan membatalkan referensi mandiri. Konsepnya mirip.
Sifat Unpin
hanya berarti MoveSafe
. Bagi saya ini bukan tentang nilai yang dapat dipindahkan dari memori di belakang Pin
. Sebaliknya, ini tentang nilai-nilai yang tidak akan "rusak" saat Anda memindahkannya.
MoveSafe
memiliki masalah yang sama dengan Move
- semua nilai jenis apapun dapat dipindahkan dengan aman. Ini hanya menjadi tidak mungkin untuk memindahkan nilai setelah disematkan.
MoveSafe
memiliki masalah yang sama denganMove
- semua nilai jenis apapun dapat dipindahkan dengan aman.
Benar, tapi artinya "aman" berbeda, seperti di UnwindSafe
. Bagaimanapun, saya akan baik-baik saja dengan Relocate
atau apa pun semacam itu.
Untuk meringkas, saya merasa bahwa nama ciri tidak boleh DePin
, Unpin
, atau apapun dengan "pin" di namanya. Bagi saya, itulah sumber utama kebingungan. Sifat ini sebenarnya bukan "jalan keluar" dari belenggu Pin
- sifat tersebut mengatakan bahwa nilainya tidak akan dibatalkan saat dipindahkan.
Saya hanya melihat Pin
dan Unpin
sebagai hal yang benar-benar terpisah. :)
Saya merasakan kebalikannya;). Ciri ini hanya bermakna sehubungan dengan Pin, yang merupakan satu-satunya jenis yang kami miliki yang secara bermakna mengekspresikan batasan pada kemampuan bergerak nilai yang mendasarinya. Tanpa Pin, Lepas Pin sama sekali tidak berarti.
Saya suka berpikir tentang menyematkan sebagai meletakkan sesuatu di papan pin. Jika Anda melepas pin sebuah benda yang disematkan ke papan, Anda bisa memindahkan benda tersebut. Menghapus pin adalah melepas pin.
Saya suka nama Unpin
.
Saya juga dapat melihat caranya! Lepas pin adalah negasi ganda, dan dapat menyebabkan kebingungan. Namun, saya bertanya-tanya seberapa sering Anda perlu menulis !Unpin
.
Satu nama lain yang bisa saya temukan untuk Lepas pin adalah Detach
. Kembali ke metafora pinboard, Anda tidak akan _Unpin_, tetapi _Detach_ Pin dari objeknya.
Saya rasa saya sangat suka DePin
! Sejauh ini ini adalah favorit saya - ringkas, jelas merupakan kata kerja dan bukan kata sifat, dan !DePin
tampaknya cukup jelas juga ("tidak dapat dilepas pinnya").
Saya pikir itu aneh bahwa jenis menerapkan Lepas pin karena nilai tidak "disematkan" - memori.
Nilainya disematkan ke memori. Tetapi nilai juga sangat penting di sini. Menyematkan memori, bagi saya, hanya tentang memastikan bahwa memori tetap dapat diredakan atau lebih, tetapi tidak dilanggar oleh mem::swap
. Menyematkan nilai ke memori adalah tentang tidak memindahkan nilai yang disematkan itu ke tempat lain, yang persis seperti itulah Pin
.
Saya juga dapat melihat caranya! Lepas pin adalah negasi ganda, dan dapat menyebabkan kebingungan. Namun, saya bertanya-tanya seberapa sering Anda perlu menulis! Lepas pin.
Saya mendengar Anda ketika Anda mengatakan Anda tidak menganggap nama itu membingungkan. Saya, dan banyak orang lain di utas ini menemukan diri kami bingung dengan penamaannya.
Saya tidak memiliki kodenya, tetapi pertama kali saya mencoba menggunakan futures, saya ingin sebuah fungsi mengembalikan impl Future<Output=T>
. Saya tidak dapat mengingat apa yang terjadi, tetapi saya segera mendapatkan kesalahan compiler yang mengeluh tentang T dan Unpin. Pertanyaan yang perlu saya jawab adalah "Apakah aman membatasi T hanya untuk Lepas pin". Dan itu membuat saya menatap ke dalam jurang selama sekitar 2 jam yang mengerikan.
"Oh, oke jadi kalau itu maksud Pin. Jadi Unpin artinya .. Box? Kenapa itu sifatnya?"
"Tunggu, impl !Unpin
? Kenapa bukan impl Pin
?"
"Benar, Pin dan Lepas ... tidak berlawanan. Keduanya sama sekali berbeda. Tunggu, lalu apa maksud Lepas Pin lagi? Kenapa disebut begitu?"
"Apa maksud !Unpin
? Bukan ... yang lain, bukan pin? Hrrrgh"
Masih satu-satunya cara ini masuk akal di kepala saya adalah dengan mengganti 'unpin' dengan 'relocatable'. "Jenis tidak dapat direlokasi dalam memori" sangat masuk akal. Namun, meskipun mengetahui apa yang dilakukan Pin , "ketik tidak melepaskan pin" membuat saya bingung.
Mengganti nama Unpin
menjadi Relocatable
(atau Relocate
) mendapat suara saya 🗳. Tapi saya akan menemukan saran lain yang lebih baik daripada Lepas pin.
Ciri ini hanya bermakna sehubungan dengan Pin, yang merupakan satu-satunya jenis yang kami miliki yang secara bermakna mengekspresikan batasan pada kemampuan bergerak nilai yang mendasarinya. Tanpa Pin, Lepas Pin sama sekali tidak berarti.
Untuk menekankan hal ini - jaminan di sekitar pin sepenuhnya di sekitar perilaku tertentu, bukan tipe . Misalnya, fungsi generator yang menghasilkan ()
dapat dengan mudah mengimplementasikan FnOnce
dengan melanjutkan berulang kali. Meskipun tipe ini mungkin tidak mengimplementasikan Unpin
- karena status hasil dapat merujuk pada dirinya sendiri, antarmuka FnOnce
(yang bergerak sendiri) sepenuhnya aman karena belum disematkan saat Anda FnOnce
it, yang diperlukan untuk membuatnya menjadi status referensi mandiri. Unpin
secara khusus tentang jenis perilaku yang dinyatakan aman setelah jenis disematkan (yaitu, memindahkannya), bukan tentang beberapa properti intrinsik dari jenis tersebut.
Ironisnya, saya hanya datang ke sini untuk berkomentar bahwa meskipun penamaan Unpin
pasti telah diperdebatkan di masa lalu, perdebatan tentangnya yang saya ingat adalah ketika kami memilih untuk mengganti Move
dengan Unpin
, yang, ingin saya katakan, merupakan peningkatan yang jelas . Dan seringkali orang baru menyadari kemudian bahwa, sementara desain saat ini merupakan peningkatan yang pasti dari apa yang sebelumnya, masih memiliki beberapa ruang tersisa untuk perbaikan lebih lanjut . Dari arah mana saya datang ini.
Melepas pin secara khusus tentang jenis perilaku yang dinyatakan aman setelah jenis disematkan (yaitu, memindahkannya), bukan tentang beberapa properti intrinsik dari jenis tersebut.
Perilaku saat disematkan adalah properti intrinsik dari tipe tersebut, sama seperti perilaku / invarian saat dibagikan. Saya membahas ini lebih detail di posting blog ini .
@RalfJung kami berbicara melewati satu sama lain. Ada kebingungan Saya melihat banyak jenis referensi mandiri "tidak dapat dipindahkan" - ini tidak akurat, mereka memiliki status tertentu yang dapat mereka masuki di mana mereka tidak dapat dipindahkan, tetapi ketika mereka berada di negara bagian lain, itu sangat aman untuk memindahkannya (dan API kami mengandalkan kemampuan untuk memindahkannya, misalnya untuk menerapkan kombinator padanya atau memindahkannya ke Pin<Box<>>
). Saya mencoba mengklarifikasi bahwa ini bukan kasus bahwa tipe ini "tidak dapat dipindahkan".
Ah, ya, maka saya setuju. Jenis !DePin
tidak selalu disematkan , dan jika tidak, ia dapat dipindahkan seperti jenis lainnya.
@cramertj @withoutboats satu hal yang belum saya yakini, apakah kalian menentang penggantian nama Unpin
? Kedengarannya Anda tidak setuju bahwa itu perlu diubah namanya, tetapi saya tidak yakin apakah Anda menentangnya.
Saya pribadi tidak berpikir masalah utama di sini terletak pada nama Unpin
dan bahwa "jika kita bisa mengganti namanya semuanya akan intuitif". Meskipun mengganti nama mungkin sedikit membantu (dan Relokasi / DePin tampak bagus di sini ...), menurut saya kerumitan utama berasal dari konsep menyematkan itu sendiri; ini jelas bukan salah satu konsep termudah untuk dipahami atau dijelaskan; bahkan tidak hampir.
Oleh karena itu, saya pikir dokumentasi dari modul core::pin
perlu ditingkatkan _significantly_ dan lebih banyak contoh perlu disertakan. Contoh yang baik akan menunjukkan kasus penggunaan kanonis serta penggunaan fungsi dan implementasi yang tidak aman yang tidak tepat.
@alexcrichton Saya tidak menentang penggantian nama, tidak. Saya pikir Unpin
sudah baik-baik saja, tetapi saya akan baik-baik saja dengan DePin
, itulah sebabnya saya menyarankannya. RemoveFromPin
adalah yang paling jelas "benar", tetapi bertele-tele ke titik garam sintaksis, jadi saya pikir saya akan menentang nama itu secara khusus. Saya, bagaimanapun, menentang untuk menunda stabilisasi tanpa batas sampai kami menemukan nama yang disetujui semua orang adalah yang terbaik-- Saya pikir API memiliki beberapa kompleksitas yang melekat yang tidak akan dibuat secara dramatis lebih baik atau lebih buruk karena nama Unpin
sifat. Saya benar-benar ingin mendorong stabilisasi segera untuk mencegah lebih banyak churn ke kode, dokumen, dan diskusi sekitar futures_api
(sehingga kita dapat mulai menyiapkan potongan untuk menstabilkan futures_api
diri). Mungkin kita harus menjadwalkan pertemuan VC penetapan nama khusus sehingga setiap orang yang memiliki pendapat dapat mengajukan solusi mereka dan kita dapat memiliki peluang bandwidth yang lebih tinggi untuk menyelesaikan ini?
Pilihan saya adalah std::pin::Reloc
(Relokasi)
Saya memiliki dua situasi yang sangat hipotetis (yah, sebenarnya hanya satu, tetapi dua eksekusi berbeda) yang saya tidak keberatan untuk menyatakan secara eksplisit apakah hal itu diizinkan atau tidak.
Unpin
menunjukkan bahwa transisi dari semua status T
(di mana T: Unpin
) adalah hal yang sepele (saya harap saya mengingat istilah itu dengan benar dari salah satu entri blog Pin
dapat membagikan &mut T
.
Jadi mari kita asumsikan saya ingin membuat tipe Woof
yang juga selalu memungkinkan untuk transisi dari semua status Woof
sementara dalam status tipe yang disematkan ke tipe yang tidak disematkan menyatakan, tetapi tidak sepele untuk melakukannya dan oleh karena itu tidak dapat mengimplementasikan Unpin
, apakah akan diizinkan untuk Woof
memiliki fungsi seperti fn unpin(self: Pin<Box<Woof>>) -> Box<Woof>
?
Sama untuk tipe Meow
yang terkadang hanya memungkinkan untuk transisi dari yang disematkan ke yang tidak disematkan dan fungsi seperti fn unpin(self: Pin<Box<Meow>>) -> Result<Box<Meow>, Pin<Box<Meow>>>
.
2 cts saya untuk bikeshedding:
Jika saya mengerti dengan benar, apa yang dimaksud Unpin
realy adalah " Pin
tidak berpengaruh pada saya".
Bagaimana dengan sesuatu seperti BypassPin
atau IgnorePin
?
Jadi mari kita asumsikan saya ingin membuat tipe Woof yang juga selalu memungkinkan untuk transisi dari semua status Woof saat dalam status tipe tersemat ke status tipe yang tidak disematkan, tetapi tidak sepele untuk melakukannya dan oleh karena itu tidak dapat mengimplementasikan Unpin, apakah akan diizinkan Woof memiliki fungsi seperti fn unpin (self: Pin
>) -> Kotak ?
Ya, itu mungkin saja. Misalnya, jika Woof
adalah elemen dari daftar tertaut yang mengganggu, ini bisa menyediakan fungsi untuk menghapus elemen dari daftar, dan menghapus Pin
pada saat yang sama (dengan alasan bahwa untuk non -dengan antrean Woof
s, kedua jenis kata tersebut setara, jadi kita dapat melepas pinnya).
Relocate
memiliki masalah yang sama dengan RePin
bagi saya, ini dapat berarti operasi yang memungkinkan untuk memindahkan nilai dari satu lokasi yang disematkan ke lokasi lain, tidak melepas nilai sepenuhnya. Ini memiliki konotasi yang kurang kuat dibandingkan dengan RePin
, tetapi masih tampak sedikit membingungkan.
ini dapat menyiratkan operasi yang memungkinkan untuk memindahkan nilai dari satu lokasi yang disematkan ke lokasi lain, tidak melepas nilai sepenuhnya.
Meskipun bukan itu yang dimaksud dengan sifat ini, ini juga tidak sepenuhnya salah: Untuk sebagian besar kasus penggunaan, memindahkan nilai dari satu lokasi yang disematkan ke lokasi lain sama berbahaya seperti menggunakannya secara bebas. Sebenarnya, mengingat siapa pun dapat membuat Pin<Box<T>>
, saya bahkan tidak mengerti mengapa Anda membuat perbedaan mendasar di sini.
Untuk sebagian besar kasus penggunaan, memindahkan nilai dari satu lokasi yang disematkan ke lokasi lain sama mengerikannya dengan menggunakannya secara bebas.
Ya, itulah mengapa memiliki sifat yang menambahkan operasi itu ke suatu tipe bisa berguna (ini tidak bisa menjadi sifat penanda seperti Unpin
karena mungkin perlu melakukan hal-hal seperti memperbarui referensi internal). Saya tidak mengusulkan agar kami menambahkan sesuatu seperti ini sekarang, hanya saja itu adalah sesuatu yang saya lihat akan diberikan di masa mendatang (baik dalam std
atau pihak ketiga) yang dapat berakhir dengan tumpang tindih nama yang membingungkan.
Saya tidak mengetahui kasus penggunaan yang kuat untuk itu sekarang, tetapi saya telah berpikir untuk menggunakan sesuatu seperti itu dengan koleksi yang disematkan.
Ok jadi inilah beberapa pemikiran. Awalnya saya bertanya-tanya apakah kita bisa memiliki nama seperti PinInert
atau PinNoGuarantees
karena itu juga deskripsi, tetapi memikirkan bahwa yang saya inginkan adalah sifat yang menggambarkan tindakan sebagai lawan dari properti, atau setidaknya membuatnya lebih mudah untuk dipikirkan di kepala saya.
Satu masalah dengan Unpin
(Saya tidak yakin mengapa) adalah bahwa saya tidak dapat memahami maksud yang dimaksud adalah "tindakan menghapus dari pin, melepas pin, aman dilakukan ". Sifat sebagaimana adanya menyampaikan tindakan "tindakan melepas pin" tetapi saya tidak bisa memahami hal itu saat membacanya. Aneh rasanya memiliki Pin<T: Unpin>
karena jika "lepas" mengapa ada di Pin?
Saya ingin tahu apakah nama seperti CanUnpin
bisa berfungsi? Banyak dari ini bukan tentang jaminan keras dengan satu atau lain cara (menerapkan Unpin
tidak berarti Anda akan menghapusnya dari pin, itu hanya berarti Anda dapat menghapusnya dari pin). Bagaimana kedengarannya? Apakah CanUnpin
cukup dapat dibaca oleh orang lain? Cukup pendek?
(memiliki awalan Can
menyampaikan kata kerja kepada saya dengan lebih mudah juga, dan dikatakan Anda dapat menghapusnya dari pin tetapi Anda tidak selalu akan selalu melakukannya).
Sebagai garis singgung lain yang tidak terkait, satu hal yang lupa saya kemukakan sebelumnya (maaf tentang itu!) Adalah saya tidak berpikir bahwa semua metode di atas harus menjadi metode yang melekat. Kami sudah memiliki banyak bug di sekitar metode inferensi dan bentrok, dan menambahkan nama yang sangat umum seperti as_ref
dan as_mut
pada jenis yang juga menerapkan Deref
sepertinya menjadi masalah menunggu untuk terjadi.
Apakah operasi ini terjadi cukup umum untuk membenarkan lokasi berisiko untuk menempatkannya? (secara inheren) Atau apakah mereka cukup jarang digunakan sehingga rute aman dari fungsi-fungsi terkait dapat dipertimbangkan?
Karena saya tampaknya memiliki skin dalam game ini sekarang: CanUnpin
sepertinya sangat dekat dengan saya dengan Unpinnable
atau yang serupa, dan kesan saya selalu bahwa komunitas tidak menyukai pengubah semacam itu di nama sifat, karena sebagian besar sifat mendeskripsikan tindakan yang dapat dilakukan oleh tipe tersebut. Implikasinya sendiri adalah "bisa" atau "-able" yang tersirat dalam pengertian itu. Apa pun yang diputuskan (dan namanya tidak menjadi masalah sejauh yang dinyatakan di sini IM-not-so-HO - sangat sedikit pengguna yang mungkin harus mengetik ini), saya akan mendorong semua orang untuk merasakan urgensi untuk menyelesaikan pertanyaan di sini. Saya senang dengan stabilisasi ini, dan saya tahu banyak orang yang menyukainya!
@alexrichard
Satu masalah dengan
Unpin
(Saya tidak yakin mengapa) adalah bahwa saya tidak dapat memahami maksud yang dimaksud adalah "tindakan menghapus dari pin, melepas pin, aman dilakukan ".
Bagaimana jika Anda menganggap T: Unpin
sebagai " T
kebal terhadap Pin
"? Kemudian jika Anda memiliki Pin<T: Unpin>
itu berarti kita memiliki nilai di dalam Pin
yang kebal terhadap pin sehingga pin di sini secara efektif diperdebatkan.
Atau dengan kata lain: Unpin
menetralkan Pin
. Sejujurnya, setelah begitu banyak diskusi saya telah menginternalisasi ini dan sekarang masuk akal. 😆
Banyak dari ini bukan tentang jaminan keras dengan satu atau lain cara (menerapkan
Unpin
tidak berarti bahwa Anda akan menghapusnya dari pin, itu hanya berarti Anda _dapat_ menghapusnya dari pin).
Sebuah tandingan untuk ini adalah sifat Send
. Ini tidak berarti bahwa Anda akan mengirimkan nilainya, itu hanya berarti Anda dapat mengirimkannya.
@anp Saya setuju bahwa CanUnpin
bukanlah nama yang bagus, saya berusaha melakukan yang terbaik untuk menemukan nama yang lebih baik! Tampaknya sedikit yang masih merasa ini perlu diganti nama meskipun karena semua saran tampaknya ditolak karena pada dasarnya alasan yang sama saya merasa seperti Unpin
perlu diganti nama di tempat pertama.
Juga sebagai pengingat, setiap stabilisasi mengendarai kereta seperti semua perubahan lainnya, dan dengan rilis minggu depan ini pasti tidak akan masuk ke rilis itu dan selanjutnya akan menjadi kandidat untuk rilis berikutnya. Itu berarti kita punya waktu 7 minggu, hampir dua bulan, untuk mendaratkan semua ini untuk memastikannya masuk secepatnya. Meskipun saya setuju bahwa urgensi itu perlu, itu hanya “jangan lupakan tentang urgensi ini”, bukan “mari selesaikan ini sebelum Senin”.
@stjepang Saya, juga, telah terbiasa menjadi Unpin
sekarang setelah terlalu memikirkannya! Semua nama alternatif pada saat ini tampak kurang bersemangat, jadi saya sampai pada titik di mana saya akan puas dengan dokumentasi yang lebih baik. Idealnya saya ingin menemukan seseorang yang tidak tahu banyak tentang Pin
apis untuk membaca dokumentasi tersebut setelahnya, meskipun, untuk mengecek ulang itu cukup untuk belajar.
Saya juga ingin melanjutkan dan secara resmi memblokir stabilisasi pada dokumentasi yang ditingkatkan, karena ini terasa sangat penting untuk masalah ini.
@rfcbot perhatian meningkatkan-dokumentasi
Secara konkret yang saya rasa membutuhkan dokumentasi yang lebih baik adalah:
P
DerefMut
implementasi "berperilaku wajar" tidak didokumentasikan pada Pin::new_unchecked
.unsafe
harus memiliki contoh kode mengapa tidak aman, atau setidaknya penjelasan yang jelas tentang urutan langkah yang bisa salah.Pin
dan apa artinya. Saya pikir mereka akan mendapatkan keuntungan dari informasi seperti "bagaimana ini bukan tipe tidak bergerak umum tetapi bentuknya" atau "bagaimana Pin
digunakan dalam praktik, misalnya dengan kontrak berjangka".Unpin
baik dalam dokumentasi atau contoh kode. Misalnya kedengarannya banyak kombinator di masa depan harus menangani ini, dan demikian pula beberapa kombinator secara khusus memerlukan ini. Beberapa contoh kasus penggunaan bagaimana seseorang bekerja dengan obat generik dan bagaimana mereka bermain dapat membantu memahami Unpin
cukup banyak.Saya juga penasaran apakah orang lain memiliki item nyata yang dapat ditindaklanjuti untuk ditambahkan ke dokumentasi juga!
Selanjutnya saya juga ingin memblokir secara resmi self
-question dengan as_ref
, tapi saya rasa ini akan cepat diselesaikan. (sekali lagi maaf karena tidak ingat untuk membicarakan ini lebih awal)
@rbot menyangkut metode mandiri
Ini mengusulkan as_ref
, as_mut
, get_ref
, get_mut
, into_ref
, dan set
untuk semua menjadi metode di Pin
jenis. Proposal menyebutkan bahwa kami tidak melakukan ini untuk petunjuk cerdas karena bentrokan metode, tetapi mengutip pengalaman yang menunjukkan bahwa Pin
API yang diterapkan hari ini tidak konsisten dan seringkali berakhir begitu saja.
Nama metode ini, bagaimanapun, adalah nama yang sangat pendek dan manis dan cukup umum ditemukan di seluruh ekosistem. Tim libs telah mengalami serangkaian bug yang tidak pernah berakhir di masa lalu di mana kami menambahkan implementasi sifat dan semacamnya dan ini menyebabkan bentrokan dengan metode sifat yang ada di berbagai peti. Metode-metode ini tampaknya berisiko tinggi karena namanya. Selain itu, tidak jelas apakah kami dapat menambahkan lebih banyak metode ke Pin
di masa mendatang karena meskipun nama-nama tersebut sekarang akhirnya berhasil, nama masa depan yang ditambahkan memiliki risiko benturan yang tinggi.
Saya hanya ingin memastikan bahwa kita memikirkan kembali perbedaan ini dari konvensi, saya secara pribadi sangat khawatir tentang konsekuensinya dan saya pikir akan sangat bagus jika kita dapat menemukan jalan tengah yang akan menghindari bahaya ini.
Dan sementara saya melakukannya, satu hal terakhir yang saya perhatikan, dan sekali lagi saya pikir ini akan cukup cepat untuk diselesaikan
@ rumahkotak-kotak-pin-pin
Apakah nama Box::pin
telah dipertimbangkan untuk Box::pinned
? Dengan adanya Pinned
dan / atau PhantomPinned
sepertinya akan rapi jika nama bisa cocok dengan tipe yang dikembalikan!
@alexcrichton Apakah ada sesuatu yang secara khusus meyakinkan Anda terhadap MoveFromPin
sebagai opsi ? Saya melihat Anda menyukai hal itu sebelumnya (dan banyak orang sepertinya menyukainya juga). Sebagai catatan, keberatan langsung yang dapat saya ingat, atau dengan cepat ditemukan dengan Ctrl + F-ing, adalah bahwa "nama yang merupakan frasa utuh ... akan menjadi sangat tidak otomatis" (@withoutboats) dan "terlalu bertele-tele" ( @jamur_kejang).
Saya harus mengakui bahwa motivasi dalam nada "Saya telah terbiasa ... sekarang setelah terlalu memikirkannya" membuat saya agak tidak nyaman. Manusia bisa terbiasa dan merasionalisasi post-hoc pada dasarnya apa saja. Tidak peduli apa namanya, akan menjadi kejutan besar jika kita akhirnya tidak terbiasa dengannya. (Inilah alasan utama mengapa kami memilih nama yang jelas -suboptimal seperti existential type
sebagai sementara dalam kasus lain, untuk mencoba menghindarinya menjadi permanen.)
Secara tidak sengaja mengistimewakan perspektif pengguna berpengalaman (yang mana kita semua!) Atas orang lain yang akan datang dengan otak yang lebih segar adalah jenis kesalahan yang menurut saya kita perlu selalu waspada terhadap pembuatan, sesulit itu .
Meskipun ini jelas bukan gaya std lib, saya juga merasa seperti CanUnpin
entah bagaimana membuatnya lebih jelas bagaimana bagian tertentu itu cocok dengan yang lain.
Tidak mungkin untuk menemukan nama yang menjelaskan arti dari Unpin
bagi seorang pemula tanpa membaca dokumentasi. Send
dan Sync
juga sulit dipahami oleh pemula, tetapi mereka terlihat bagus dan ringkas, serta Unpin
. Jika Anda tidak tahu apa arti nama-nama ini, Anda harus membaca dokumen mereka.
@valff Memang benar, namun ada beberapa orang yang telah membaca dokumen dan memahami cara kerja pin, namun nama Unpin
masih menyebabkan mereka mengalami kesulitan mental yang signifikan, sedangkan nama lain menyebabkan lebih sedikit.
@glaebhoerl oh maaf untuk menjelaskan. Saya pribadi lebih menyukai MoveFromPin
atau CanUnpin
daripada Unpin
. Hanya mencoba membantu mencapai kesimpulan!
Saya mencoba memahami proposal saat ini, mungkin laporan kemajuan semacam ini dapat memberi penerangan tambahan pada penamaan juga.
Pin
ke alamat yang stabil hingga Drop
memberikan persyaratan tambahan pada pembuat Pin
. Saya akan merasa berguna jika prasyarat yang diperlukan untuk meminta unsafe fn new_unchecked(pointer: P) -> Pin<P>
disebutkan secara langsung. Memahami bagaimana Pin
dapat dibuat sangat membantu dalam memahami konsep mereka, imo.we agreed that we are ready to stabilize them with no major API changes.
, berisi banyak api lama. Pada saat yang sama, ini juga berisi banyak wawasan tentang Drop
dan proyeksi pin yang relevan. Ini juga berisi rumusan eksplisit tentang jaminan tambahan sebagaimana dimaksud dalam butir satu yang tidak ditemukan dalam laporan ini. Campuran informasi lama dan baru ini agak membingungkan, pertimbangkan untuk memindahkan semua informasi ke dalam masalah ini atau menunjukkan paragraf yang sudah ketinggalan zaman.slight extension
, saya berharap ada semacam cara untuk tidak ikut serta. Karena penyematan sekali membawa status tipe pada penunjuk tersebut hingga dilepaskan, beberapa cara untuk kembali dari status tipe ini ke status 'normal'. Sebenarnya, itulah yang saya pikir Unpin
lakukan pada awalnya tapi saya pikir Unpin
sebenarnya sedikit lebih kuat. Dikatakan bahwa status tersemat dapat dengan bebas diubah menjadi status unpinned, sesuka hati. Tidak masalah jika antarmuka saat ini minimal dalam hal ini, tetapi Unpin
dapat dikacaukan dengan operasi pada jenis yang menghapus beberapa invarian dalam yang memerlukan status pin (seperti versi lunak Drop
) .Klarifikasi: Saya pribadi lebih menyukai MoveFromPin
daripada nama lain tetapi bisa dibilang artifisial dan kikuk.
Item utama yang dapat ditindaklanjuti yang dapat ditindaklanjuti yang terlintas dalam pikiran, selain perbaikan dokumentasi yang sudah diminta oleh @alexcrichton , akan menjelaskan alasan di balik aturan bidang untuk proyeksi pin di map_unchecked
dan map_unchecked_mut
. Sesuatu di sepanjang baris:
Pin berjanji untuk tidak memindahkan data yang dirujuk hingga dijatuhkan. Karena bidang dari struct dihapus setelah struct yang berisi, status pin mereka melampaui
Drop
implementasi dari struct itu sendiri. Memproyeksikan ke sebuah field berjanji untuk tidak berpindah darinya di dalam structs destructor.
Bagaimana dengan nama ElidePin
untuk sifat penanda turunan otomatis?
Ini akan menangkap bahwa jenis selalu dapat diperlakukan seolah-olah atau berperilaku seolah-olah tidak disematkan. Dan !ElidePin
tampaknya cukup jelas juga, meskipun itu mungkin subjektif.
Penanda standar juga tidak memiliki nama yang sempurna untuk memahami penyematan. Pinned
tampaknya membangkitkan gagasan bahwa struct yang berisi itu sendiri disematkan tetapi penyematan berlaku untuk pointer dan hanya setelah tindakan eksplisit. Ini tidak terlalu penting tetapi juga tidak signifikan, terutama karena ini bisa menjadi tipe yang pertama kali ditemukan dalam implementasi untuk tipe referensi sendiri.
Penentangan umum saya terhadap MoveFromUnpin
/ RemoveFromUnpin
hanyalah karena mereka terlalu bertele-tele dan akan menurunkan ergonomi implementasi manual Future
. CanUnpin
/ DePin
keduanya tampak baik-baik saja bagi saya-- Saya berharap lebih jelas bahwa Unpin
mengikuti pola Read
/ Write
/ etc. tetapi tampaknya itu tidak intuitif bagi orang-orang jadi saya akan memberi +1 pada apa pun yang membuat ini lebih jelas tanpa terlihat seperti garam sintaksis.
Saya setuju NotWhateverUnpinBecomes
mungkin adalah nama terbaik untuk Pinned
. Yang mengatakan, Adhere
berarti memperhatikan dan tetap berpegang pada. : sedikit_muka_senyum:
CanUnpin / DePin keduanya tampak baik-baik saja bagi saya-- Saya berharap lebih jelas bahwa Lepas pin mengikuti pola Baca / Tulis / dll
Saya pikir salah satu hal yang membuat Unpin
sulit, tidak seperti Read
adalah bahwa ini adalah ciri penanda. Read
mudah dimengerti karena ada metode Read::read
- semua yang perlu Anda ketahui ada di trait
. Jika x
adalah Read
Saya mengerti bahwa saya dapat menelepon x.read()
- sama halnya dengan write
untuk Write
, dll. Lebih sulit untuk menjelaskan bahwa X
mengimplementasikan Unpin
berarti Pin<Ptr<X>>
mengimplementasikan DerefMut
- yang berarti Anda dapat memperlakukannya seolah-olah hanya X
.
Baca mudah dipahami karena ada metode Read :: read
Jika saja kita dapat menambahkan metode yang selalu berlaku di auto trait
definisi + GAT, kita dapat memiliki Unpin::unpin
- fn unpin<P: DerefFamily>(self: Pin<P::Deref<Self>>) -> P::Deref<Self>
). ..... setelah dipikir-pikir, saya tidak berpikir itu akan membuat orang kurang bingung;)
(pada catatan yang lebih serius, saya mendukung Pin::unpin
untuk beralih dari Pin<P<T>>
menjadi P<T>
)
Saya akan mendukung Pin :: unpin untuk beralih dari Pin
> ke P
Ini membingungkan dua istilah di kepala saya, seperti nama itu sendiri. unpin
terdengar sangat mirip dengan membalikkan jaminan status tipe, untuk perbandingan yang seolah-olah ada metode fn unborrow(&'_ T)
atau fn unmove(??)
. Karena pin
memberikan jaminan sampai beberapa memori yang mewakili sebuah tipe adalah Drop::drop
ed, kami tidak benar-benar membalikkan keadaan, tipe tersebut hanya menjamin bahwa semua representasi lain memberikan jaminan yang setara dan dengan demikian kami dapat mengabaikan ini . Ini juga merupakan perbedaan utama yang saya lihat antara ciri-ciri penanda dan sesuatu seperti io::Read
. Satu mengaktifkan operasi untuk kompilator atau bahasa, sementara yang lain mengaktifkan operasi untuk pemrogram.
Selain itu, ini adalah poin utama yang saat ini tidak dapat disajikan secara akurat hanya dengan menggunakan pengetikan yang benar. Untuk memanggil operasi unpin
membuatnya terdengar seperti ada fungsi sebaliknya pin
. Ini menjadi fungsi yang juga sedikit salah mengisyaratkan bahwa operasi pelepasan pin semacam itu terikat pada pekerjaan komputasi, misalnya into_pointer()
akan lebih baik dalam hal ini dengan juga mengikuti konvensi penamaan yang lebih mapan.
Terakhir, saya pikir ada potensi untuk tipe yang secara khusus memiliki fungsi unpin
dengan pekerjaan komputasi. Jenis dengan invarian dalam yang sangat khusus mungkin dapat 'memperbaiki' keadaan dalamnya sedemikian rupa sehingga ia dapat menawarkan antarmuka fn unpin(self: Pin<&'a mut T>) -> &'a mut
, di mana ia rela kehilangan semua jaminan Pin
untuk seumur hidup 'a
. Dalam hal ini, kedua poin di atas tidak berlaku lagi. Fungsi seperti itu dapat dibayangkan seolah-olah memiliki efek yang setara dengan menjatuhkan dan merekonstruksi di lokasi memori yang sama (sehingga benar-benar menghapus status tipe). Dan ini mungkin melibatkan komputasi, misalnya dengan memindahkan beberapa referensi mandiri ke dalam alokasi dinamis.
Akan sangat disayangkan jika nama yang membingungkan juga akan mempersulit desainer dan pelaksana perpustakaan untuk memilih nama yang tidak membingungkan, dengan menggabungkan kedua ide ini.
Penentangan umum saya terhadap MoveFromUnpin / RemoveFromUnpin hanya karena mereka terlalu bertele-tele dan akan menurunkan ergonomi implementasi manual Future.
Saya rasa itu tidak benar, terutama untuk MoveFromPin
, yang tampaknya masuk akal untuk saya ketik - dan dengan segala jenis pelengkapan otomatis yang cerdas atau bodoh, masalahnya sebagian besar tidak ada.
Salah satu bagian penting dari ergonomi harus juga keterbacaan dan pemahaman kode. Rust sudah mendapatkan sedikit kritik di masa lalu karena menyingkat hal-hal secara agresif ( fn
, mut
, dll), yang membuat beberapa kode lebih sulit untuk dibaca. Untuk hal-hal yang untuk konsep yang bahkan lebih rumit untuk dipahami dan yang memenuhi tujuan khusus bagi sebagian besar pengguna, penggunaan nama yang lebih verbose dan deskriptif harus diterima sepenuhnya.
@rfcbot menyelesaikan penamaan-dari-Lepas pin
Oke, saya sudah merebus cukup lama sekarang dan juga berbicara dengan @withoutboats secara langsung tentang hal ini. Saya menyadari bahwa sekarang saya secara pribadi merasa nyaman setidaknya dengan nama Unpin
sebagai ciri penanda. Akibatnya, saya akan menghapus keberatan pemblokiran (meskipun jika orang lain di tim libs merasa berbeda, beri tahu kami!)
Alasan utama mengapa saya sampai pada Unpin
pada dasarnya adalah apa yang telah dikatakan @cramertj di atas , itu adalah nama paling idiomatik untuk sifat ini yang dapat kami temukan. Semua nama sifat lain yang saya lihat diusulkan tidak memenuhi standar idiomatik yang sesuai dengan Unpin
(atau setidaknya menurut saya).
Meskipun saya masih percaya ada nama yang lebih baik untuk "Saya baru saja melihat nama dari sifat ini dan saya ingin tahu apa fungsinya", saya rasa itu bukan masalah yang tepat untuk diselesaikan di sini. Jelas bagi saya pada saat ini bahwa kita tidak dapat, setidaknya untuk saat ini, menemukan nama lain untuk sifat ini yang juga idiomatis, tetapi kita beralih pada nama sifat yang memecahkan masalah yang berbeda. Memahami Unpin
sama seperti Send
dan Sync
, dokumentasi perlu dibaca untuk memahami sepenuhnya apa yang terjadi.
Sebagai poin klarifikasi lain, saya mencantumkan beberapa "keberatan yang memblokir" tetapi itu lebih banyak item TODO daripada memblokir keberatan itu sendiri. Saya hanya tidak memiliki cara yang bagus untuk menavigasi utas yang begitu panjang! Dalam hal itu saya pikir tidak apa-apa untuk PR stabilisasi untuk diposting kapan saja sekarang dengan FCP telah berakhir selama seminggu atau lebih sekarang. Poin terakhir tentang diri / pin / pin dapat didiskusikan secara singkat di sana (jika perlu, mereka dapat ditinggalkan seperti proposal di atas).
Dokumentasi menurut saya khususnya bukanlah prasyarat untuk stabilisasi dalam kasus ini. Kami memiliki siklus penuh (6 minggu) untuk menambahkan dokumen sebelum jenis ini mencapai stabil dan kami punya waktu lebih lama untuk menopang dokumentasi di sini sebelum kisah async / await lengkap mencapai stabil. Banyak sekali waktu untuk meningkatkan apa yang ada sekarang, dan apa yang ada sekarang sudah cukup berguna!
Apa artinya "idiomatik" di sini? Apa yang tidak otomatis tentang Reloc[ate]
, Escape
, Evade
, Pluck
, atau Roam
? Mereka semua adalah kata kerja, dan tidak ada satupun yang dapat disalahartikan sebagai memiliki masalah negatif ganda.
@alexcrichton Apakah ada alasan mengapa Unpin
dianggap lebih idiomatis daripada Depin
atau DePin
?
Saya pikir Depin
adalah alternatif yang sangat solid, dan itu tidak menyebabkan saya mengalami kesulitan mental yang sama seperti Unpin
.
Salah satu alasan sederhananya adalah depin bukanlah sebuah kata dan lepas pin adalah. Secara pribadi, saya tidak kesulitan memahami Unpin
. Saya pikir itu cocok dengan penamaan ciri penanda lainnya.
@jimmycuadra Ada banyak nama di Rust yang bukan kata-kata "asli", termasuk di stdlib.
Saya akan terkejut jika itu dianggap sebagai alasan penting untuk memilih satu nama di atas nama lainnya.
@Pauan Itu adalah alasan yang signifikan. Sebagai penutur asli bahasa Inggris, Depin
terdengar seperti kami lupa bahwa kata untuk melepas pin ada dan mencoba membuatnya. Tampaknya sangat salah bagi saya secara tata bahasa: kata bahasa Inggris untuk "depinning" adalah "unpinning."
Sebuah analogi yang baik adalah jika kita memiliki API yang mengatakan "delock" bukan "unlock".
@jaredr oleh "idiomatic" Maksud saya mengikuti konvensi mapan dari perpustakaan standar sudah seperti (disebutkan sebelumnya) Read
dan Write
sifat. Kami memiliki konvensi nama yang tidak bertele-tele, kata kerja, singkat jika memungkinkan, dan sesuai untuk situasi.
Nama-nama seperti yang Anda sarankan semuanya mungkin, tetapi Unpin
(atau setidaknya saya rasa) adalah yang paling sesuai untuk tindakan khusus ini (jaminan "Anda dapat melepas pin jenis ini"). Yang lain, sementara sinonim longgar Unpin
, saya pikir sebagian besar hanya mengganti nama dari kata "lepas pin" dan terkadang tidak menyampaikan hubungan yang sama dengan Pin
sebagai Unpin
tidak.
@Pauan Saya setuju dengan @withoutboats bahwa Depin
tidak terdengar seperti jives dan juga Unpin
.
@aturon mencatat di https://github.com/rust-lang/rfcs/pull/2592#issuecomment -438873636 bahwa:
Penggunaan
Pin
tidak lebih merupakan detail implementasi daripada pilihan&self
versus&mut self
; dalam jangka panjang, kemungkinanPin
itu sendiri akan menjadi mode referensi terpisah dengan dukungan bahasa. Dalam semua kasus ini, apa yang sedang disampaikan dalam tanda tangan adalah izin yang diberikan kepada callee, denganPin
melarang pemindahan referensi.
Tampaknya ini mengacu pada tipe asli &pin
atau semacamnya ... tapi saya tidak tahu bagaimana kotak ini dengan Pin<&mut T>
dan semacamnya. Dalam percakapan saya dengan @withoutboats dan khususnya @cramertj, mereka sama sekali tidak yakin tentang gagasan mode referensi terpisah dengan dukungan bahasa dan bagaimana kami bisa sampai di sana dari Pin<T>
.
Sebelum menstabilkan pin
, sebaiknya rekonsiliasi pandangan ini untuk memastikan bahwa kita berada di halaman yang sama. infrastruktur penting ini; jadi saya terutama ingin Aaron mengembangkan ini dan untuk perahu dan Taylor untuk kemudian ditimbang.
Yang lain, sementara sinonim lepas dari Lepas pin, saya pikir sebagian besar hanya mengganti nama dari kata "lepas pin" dan terkadang tidak menyampaikan hubungan yang sama dengan Pin seperti Lepas Pin.
@alexcrichton ini sebenarnya hal yang baik, menurut saya? Seperti Send
untuk memindahkan / menyalin (=) operasi, Sync
untuk meminjam (&) operasi. Mungkinkah koneksi seperti itu sebenarnya menyebabkan lebih banyak kebingungan?
@ crl0710 mungkin! Saya tidak yakin saya akan setuju dengan koneksi seperti itu meskipun saya sendiri. Send
dan Sync
lebih banyak tentang apa yang mereka aktifkan ("mengirim" tipe ke utas lain dan "menyinkronkan" akses ke utas), dan ketika menamainya kami tidak mencoba untuk hindari menamainya dekat dengan satu operasi atau lainnya
@alexcrich persis! jadi mungkin sifat ini juga harus tentang apa yang memungkinkannya. ("bergerak (Kata kerja di sini) dari Pin
). Saya bukan penutur asli bahasa Inggris, tapi menurut saya "membongkar" dari pin
itu sedikit ... aneh?
@ crlf0710 Tapi apa yang mengaktifkan sifat itu bukanlah <moving>
dari Pin, itu <moving out of a Pin>
. Masalah dengan move dan sinonim untuk move adalah bahwa sifat tersebut menyiratkan sifat mengontrol kemampuan tipe untuk bergerak sama sekali, yang tidak dilakukannya. Hubungan dengan Pin sangat penting untuk memahami apa yang sebenarnya dilakukan oleh sifat tersebut.
Jadi, nama yang paling idiomatis untuk sifat ini adalah kata kerja yang berarti "keluar dari peniti", di mana kata "Lepas pin" adalah yang paling jelas bagi saya. Berikut definisi Wiktionary tentang "lepas pin":
- Untuk membuka dengan melepas pin.
- (Transitif, komputasi, antarmuka pengguna grafis) Untuk melepaskan (ikon, aplikasi, dll.) dari tempat sebelumnya disematkan.
@withoutboats terima kasih telah menjelaskan! Sebenarnya saya pikir saya dapat menerima nama Unpin
atau nama apa pun yang akhirnya diputuskan oleh tim karat, saya tidak ingin memblokir stabilisasi jenis Pin sama sekali, dan saya benar-benar memahami kekhawatiran tentang "pemindahan" dll.
Rasanya hanya ada sedikit "pengulangan" di suatu tempat. dan saya harus meyakinkan diri saya sendiri: itu tidak berarti "tidak dapat diubah", tetapi sebaliknya. Saya kira setelah beberapa waktu saya akan terbiasa jika itu keputusan akhir.
Pada saat yang sama izinkan saya memberikan saran terakhir saya: sebenarnya menurut saya kata kerja Detach
disebutkan di atas juga cukup bagus, meskipun agak terlalu umum. Seperti yang saya katakan, saya bukan penutur asli, jadi saya tidak bisa berbicara untuk orang lain. Tolong anggap saja sebagai ide kecil.
Saya berpikir tentang proyeksi disjoint yang aman, dan muncul dengan ide yang memungkinkannya. Ini adalah makro untuk menyimulasikan pencocokan terhadap nilai l yang bisa berubah yang disematkan. Dengan makro ini kita bisa menulis
pin_proj! { let MyStruct { field1, field2 } = a_pinned_mutable_reference; }
untuk mendekomposisi Pin<&mut MyStruct>
menjadi referensi yang bisa diubah yang disematkan ke bidang.
Untuk membuat ini aman dan dapat digunakan, kita membutuhkan dua hal lain:
Kite
untuk menandai bidang "selalu- Unpin
"Unpin
tidak amanYang terakhir diperlukan untuk keamanan proyeksi. Tanpa ini, kita dapat mendefinisikan " Kite
dengan proyeksi tersemat" dengan aman, yang sebenarnya salah.
Dengan pemikiran ini, saya mengusulkan untuk membuat Unpin
tidak aman sebelum stabilisasi untuk meninggalkan ruang untuk operasi berguna lainnya agar aman.
@qnighy Ada kemungkinan untuk suatu tipe melanggar jaminan penyematan dalam implementasi Drop
, yang membuat Drop
sama kuatnya dengan Unpin
. Membuat Unpin
tidak aman tidak membuat kode tambahan menjadi aman, karena Drop
juga aman, dan tidak dapat diubah. Kami telah banyak membicarakan hal ini tentang masalah pelacakan, dan itu telah disebutkan di utas ini juga.
Pada dasarnya, proyeksi pin tidak dapat dibuat aman tanpa kemampuan untuk menegaskan batas negatif tertentu yang tidak dapat diungkapkan di Rust saat ini.
@bayu_joo
Pada saat yang sama izinkan saya memberikan saran terakhir saya: sebenarnya menurut saya kata kerja Detach yang disebutkan di atas juga cukup bagus, meskipun agak terlalu umum. Seperti yang saya katakan, saya bukan penutur asli, jadi saya tidak bisa berbicara untuk orang lain. Tolong anggap saja sebagai ide kecil.
Melepas tampaknya jauh lebih baik daripada nama seperti Pindah dan Relokasi, tetapi tampaknya bertentangan dengan kemungkinan penggunaan metafora pelepasan lainnya (mirip dengan bagaimana Escape dapat bertentangan dengan "analisis pelarian" dll). Hanya ada begitu banyak metafora yang kita miliki tentang bagaimana data dapat dikaitkan dengan data lain dalam ilmu komputer, yang membuat saya memikirkan keuntungan baru lain dari Lepas Pin: dengan berpisah erat pada metafora "pin", ia tidak menempati ruang untuk bahasa metafora di masa depan kita mungkin perlu menggunakan untuk tujuan yang berbeda, seperti nama-nama seperti Detach atau Escape atau Relocate bisa.
@withoutboats Saya tidak memiliki preferensi khusus untuk nama atau banyak investasi dalam bikeshed ini ... tapi saya penasaran; apa tujuan lain yang cocok Detach
(jika Anda berspekulasi ...)?
@Centril Saya tidak memiliki kasus penggunaan yang jelas dalam pikiran, tetapi saya dapat membayangkan beberapa kasus penggunaan yang terkait dengan perusakan, misalnya.
@withoutboats Ya itu masuk akal; Bersulang!
@withoutboats Saya tidak yakin apakah entri Wiktionary adalah motivasi terbaik untuk membenarkan penamaan Unpin
untuk mengaktifkan move from a pin
. Metafora representasi fisik menderita karena fakta bahwa bergerak di Rust tidak menghilangkan objek di dunia. Untuk lebih tepatnya, 'upinning' pointer yang disematkan tidak memberi saya kendali atas memori yang dirujuk, alokasi dan validitasnya sebagai representasi objek T
masih diperlukan dan dijamin oleh semantik pointer. Oleh karena itu, melepas pin tidak memberikan representasi terkontrol penuh dari T
, seperti yang dilakukan unboxing. Dan, entri kamus yang mendefinisikan unpinning
dalam istilah moving
sebenarnya adalah bagian dari kebingungan lebih dari sekadar pembenaran nama seperti itu.
Dalam API yang diusulkan, tidak ada metode yang memiliki efek seperti itu (dan juga tidak dalam lingkup pin). Saya tidak melihat cara yang aman misalnya untuk melakukan transisi Pin<Box<T>>
ke Box<T>
(dan dengan ekstensi ke T
) dan saya juga tidak yakin apakah Unpin
harus memiliki kemungkinan yang begitu kuat. Saya tidak yakin di mana semua perbedaannya. Tapi sejauh yang saya mengerti, Pin
berlaku untuk beberapa lokasi memori sementara Unpin
menjamin bahwa tidak ada dalam representasi T
s bergantung pada jaminan pin. Namun, apakah itu sama dengan bisa mengeluarkan dan melupakan memori sepenuhnya? Saya pikir tidak. Saya akan memikirkan contoh yang lebih konkret.
Sunting: Lebih konkretnya, bahkan jika T
adalah Unpin
, dapatkah saya mengandalkan beberapa contoh T::drop(&mut)
dipanggil pada memori yang disematkan, sebelum memori itu dibatalkan alokasinya? Sejauh yang saya tahu dari formalisme, jawabannya harus ya, tetapi nama Unpin
mengkomunikasikan sebaliknya kepada saya.
Edit 2: Rc
memungkinkan seseorang untuk mengamati Pin<&T>
tetapi untuk T: Unpin
masih belum memiliki drop
dipanggil ke lokasi memori asli. Buat referensi tetap hidup di luar pin, kemudian setelah pin dijatuhkan Anda dapat keluar dengan Rc::try_unwrap
. https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=dec6f6c6d2c0903d87a4a9cefe50a0ca Itu secara efektif menjawab pertanyaan melalui mekanisme yang ada, tetapi apakah berfungsi sebagaimana mestinya?
Mungkin IgnorePin
? Jika T
adalah IgnorePin
Anda dapat memperlakukan Pin<Box<T>>
sebagai &mut T
pada dasarnya mengabaikan keberadaan Pin
(AIUI juga mengabaikan Box
). Untuk mengabaikan adalah kata kerja, saya kira IgnorePin
bukan tapi saya tidak yakin apa itu. IgnorePin
adalah deskriptif tentang apa yang diizinkan, ini tidak menjelaskan batasan yang ditempatkan pada tipe, tetapi juga tidak Unpin
.
@wmanley Saya memiliki ide yang sangat mirip, ElidePin
, dalam beberapa komentar di atas meskipun saat itu saya belum dapat mengungkapkan secara konkret mengapa yang satu itu terasa lebih tepat. Tapi saya setuju bahwa 'satu kata kerja' adalah panduan gaya untuk ciri penanda Rust. Meskipun ini juga memungkinkan negasi yang lebih alami dalam bentuk !ElidePin
/ !IgnorePin
, ini tidak optimal.
@withoutboats Pertanyaan tindak lanjut: Karena pin tampaknya ditentukan dalam kaitannya dengan memori yang mendasarinya, bagaimana Pin berinteraksi dengan ZST? Karena Pinned
menjadi ZST, bahkan ZST bisa jadi Unpin
atau tidak. Saya akan secara intuitif mengatakan bahwa representasi memorinya tidak pernah secara resmi dibatalkan, jadi T::drop(&mut self)
tidak perlu dipanggil, sangat mirip dengan bagaimana seseorang dapat membuat pin dari memori &mut 'static _
misalnya dari Box
bocor
Apakah aman membuat Pin<P<T>>
dengan T: !Unpin
dan kemudian segera melepas pin nilainya, jika dijamin tidak ada yang memperhatikan pemasangan pin? Apakah ini hanya perilaku tidak terdefinisi jika nilai yang disematkan dipindahkan setelah metode mirip polling dipanggil?
@HeroicKatora Sayangnya, hari ini mungkin untuk mengimplementasikan Drop
untuk tipe yang bisa menjadi !Unpin
karena obat generik, misalnya:
struct S<T>(T); // `!Unpin` if `T: !Unpin`
impl<T> Drop for S<T> { ... }
@cramertj Terima kasih, baru saja menyadarinya.
@cramertj Jadi defaultnya ke T: ?Unpin
pada batas umum? Apakah ada alasan lain di balik tidak default ke T: Unpin
untuk tipe yang sudah ada seperti Sized
? Ini akan memberikan beberapa contoh di mana itu akan menjadi sangat ketat tetapi dapatkah tambahan otomatis itu menyebabkan regresi?
@HeroicKatora Itu akan membutuhkan taburan ?Unpin
batas pada setiap jenis di perpustakaan standar dan dalam sekumpulan kode yang tidak peduli tentang Unpin
sama sekali.
Ini juga aman untuk menambahkan bidang PhantomPinned, sebagai cara lain untuk membuat jenis Jatuhkan +! Lepas pin.
Apakah ada alasan lain di balik tidak default ke T: Lepas pin untuk tipe yang ada?
Lepas pin hanyalah sifat otomatis seperti Kirim dan Sinkronkan, proposal ini tidak melibatkan fitur bahasa baru. Jadi ia memiliki semantik yang sama dengan sifat otomatis yang sudah didefinisikan, yaitu bahwa sifat tersebut tidak diterapkan ke generik secara default (tidak seperti Sized, yang merupakan sifat bawaan untuk kompilator, bukan sifat otomatis).
Apakah ini hanya perilaku tidak terdefinisi jika nilai yang disematkan dipindahkan setelah metode mirip polling dipanggil?
Kami harus menjelaskan di sini bahwa perilaku tidak terdefinisi dalam konteks ini sebenarnya bukanlah jenis UB yang tradisional. Sebaliknya, kode itu, mengamati jenis dalam Pin, dapat membuat asumsi tentang semantik kepemilikan dari nilai itu (yaitu, jika tidak mengimplementasikan Lepas pin, itu tidak akan dipindahkan lagi atau tidak valid sampai destruktornya telah berjalan). Ini bukan UB dalam arti diasumsikan oleh kompilator tidak akan pernah terjadi (kompilator tidak tahu apa itu Pin).
Jadi ya, dalam artian Anda dapat memverifikasi bahwa tidak ada kode yang bergantung pada jaminan yang Anda berikan. Tetapi juga apa yang telah Anda lakukan tentu saja tidak ada gunanya, karena tidak ada kode yang dapat mengamati bahwa tipe itu disematkan.
@cramertj Begitu , itu akan sedikit disayangkan. Saya agak tidak nyaman dengan Pin
interaksi dengan Drop
dalam spesifikasi tetapi tidak dalam bahasa. Cukup bertentangan antara kebenaran antarmuka, kegunaan, dan merilisnya dalam waktu dekat. Bisakah saya mendapatkan klarifikasi tentang Sunting 2 di atas:
Rc memungkinkan seseorang untuk mengamati Pin <& T> tetapi untuk T: Unpin masih belum memanggil drop di lokasi memori asli. Biarkan referensi tetap hidup di luar pin, kemudian setelah pin dijatuhkan Anda dapat keluar dengan Rc :: try_unwrap. https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=dec6f6c6d2c0903d87a4a9cefe50a0ca
@HeroicKatora Masalah dengan kode Anda adalah Mem<'a>: Unpin
, tetapi baris kode tidak aman yang Anda gunakan bergantung pada asumsi yang hanya berlaku untuk jenis yang tidak menerapkan Unpin
. Saya telah mengedit inti Anda untuk menyertakan bukti bahwa Mem<'a>: Unpin
: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=201d1ae382f590be8c5cac13af279aff
Anda mengandalkan Lepas Pin terikat saat memanggil Pin::new
, yang hanya dapat dipanggil pada penunjuk yang targetnya mengimplementasikan Lepas Pin.
Celah yang Anda pikir Anda temukan telah dipertimbangkan, itulah sebabnya tidak ada cara untuk mengubah dari Rc<T: ?Unpin>
menjadi Pin<Rc<T>>
. Anda harus membangun Rc di pin dengan Rc::pinned
.
@withoutboats Hanya ingin mendapatkan konfirmasi itu, bahwa T: Unpin
memang juga menyisih dari panggilan drop
sebelum pembatalan. Itu menimbulkan pertanyaan, haruskah ada juga
fn into_inner(Pin<P>) -> P where P: Deref, P::Target: Unpin;
karena tidak melindungi bagian mana pun dari antarmuka, dan membuka bungkus Pin<Box<T>>
menjadi Box<T>
berpotensi berguna (tentu saja untuk T: Unpin
).
@HeroicKatora Anda benar bahwa fungsi itu akan aman. Saya tidak keberatan menambahkannya tetapi saya ingin menghindari perluasan API yang kami stabilkan saat ini , karena utas ini sudah berisi ratusan komentar. Kami selalu dapat menambahkannya nanti saat diperlukan seperti yang kami lakukan dengan semua API std.
Saya hanya akan mengatakan bahwa kedua penamaan dan fungsi dari Unpin
membuat lebih banyak dirasakan akal dengan metode berkomunikasi. Dan mengetahui bahwa jaminan Pin
memang terbatas pada T: !Unpin
. Itu juga akan secara langsung ditunjukkan dengan adanya metode seperti itu, alih-alih membangun pintu keluar untuk menunjukkan kemungkinan: smile:. Dan dokumentasinya akan menjadi tempat yang tepat untuk menjelaskan (atau menautkan sekali lagi ke) pembatasan. (Seseorang bahkan mungkin mempertimbangkan untuk menamakannya unpin
daripada into_inner
).
Sunting: Ini sebagian besar hanya akan menggeneralisasi apa yang sudah ada.
impl<P> Pin<P> where P: Deref, P::Target: Unpin
fn unpin(self) -> P
memiliki instantiation untuk P=&'a mut T
, yang setara dengan yang diusulkan
impl<'a, T: ?Sized> Pin<&'a mut T> where T: Unpin
fn get_mut(self) -> &'a mut T
Sekarang setelah stabilisasi berjalan, inti dari PFCP tampaknya diperdebatkan, jadi oleh karena itu:
@rhmhhh batal
Apakah ada pelacakan untuk memastikan komentar yang dikembangkan mulai dari https://github.com/rust-lang/rust/issues/55766#issuecomment-437374982 diubah menjadi dokumen? Sepertinya kami sudah terlambat untuk rilis karena beta telah bercabang ... meskipun itu secara eksplisit dipanggil di sini untuk terjadi sebelum stabilisasi: /
Apakah ada alasan mengapa ini masih terbuka? @Centril Anda menutup dan membuka kembali ini, apakah itu disengaja?
@RalfJung Saya mencoba untuk membatalkan FCP tetapi tidak mendengarkan; @alexcrichton Bisakah Anda membatalkan FCP?
Ini stabil, jadi tutup.
Komentar yang paling membantu
Apakah seseorang ingin menulis bab nomicon tentang masa depan? Tampaknya sangat penting mengingat betapa halusnya hal ini. Secara khusus saya pikir contoh, terutama contoh implementasi buggy / buruk dan bagaimana mereka tidak sehat, akan mencerahkan.