Rust: Masalah pelacakan untuk sifat TryFrom/TryInto

Dibuat pada 5 Mei 2016  ·  240Komentar  ·  Sumber: rust-lang/rust

Masalah pelacakan untuk https://github.com/rust-lang/rfcs/pull/1542


Melakukan:

B-unstable C-tracking-issue T-libs

Komentar yang paling membantu

Saya ingin ! menjadi tipe kosakata yang cukup sehingga Result<_, !> akan secara intuitif dibaca sebagai "hasil yang tidak dapat salah", atau "hasil yang (secara harfiah) tidak pernah Err." Menggunakan alias (apalagi jenis yang terpisah!) menurut saya agak berlebihan dan saya dapat melihatnya menyebabkan setidaknya untuk diri saya sendiri jeda sesaat ketika membaca tanda tangan jenis—"tunggu, bagaimana ini berbeda dari ! lagi ?" YMMV, tentu saja.

Semua 240 komentar

Apakah ada cara untuk mencetak kesalahan secara umum dengan nilai asli jika konversi gagal tanpa memerlukan Clone sehingga metode terkait yang panik saat gagal dapat memiliki pesan kesalahan yang bagus?

Sebuah diskusi tentang apakah ini harus masuk pendahuluan tertunda ketika ini berjalan stabil .

Maaf jika ini dibahas di tempat lain, tetapi apa yang ingin kami lihat sebelum menandai ini sebagai stabil? Saya cukup yakin saya telah menerapkan kembali fungsi ini beberapa kali di berbagai proyek, jadi sifat umum yang dapat digunakan kembali akan membuat saya

Hal ini dapat kita diskusikan untuk siklus berikutnya.

Masalah ini sekarang memasuki periode komentar akhir siklus panjang untuk stabilisasi

Sebagai titik stabilisasi, tim libs juga ingin menambahkan ciri-ciri ini ke pendahuluan sebagai bagian dari stabilisasi mereka. Ini akan membutuhkan operasi kawah yang 100% bersih _minimal_, tetapi kami relatif yakin bahwa status penyelesaian saat ini membuatnya kompatibel ke belakang untuk menambahkan ciri ke pendahuluan.

Saya punya beberapa pertanyaan tentang tujuan sifat-sifat ini.

  1. Jenis apa dalam std yang akan diimplementasikan?
  2. Jenis mana yang harus mendapatkan implementasi seperti impl TryFrom<T> for T ?
  3. Jenis mana yang harus mendapatkan implementasi seperti impl TryFrom<U> for T jika sudah memiliki impl From<U> for T ?

cc @sfackler , dapatkah Anda memperluas rangkaian impls saat ini dan beberapa alasannya juga?

Saya pikir secara umum intuisi harus persis sama dengan From / Into , kecuali ketika konversi bisa gagal. Sama seperti kita secara bertahap menambahkan implementasi From dan Into ke tipe perpustakaan standar, saya berharap kita akan melakukan hal yang sama untuk TryFrom dan TryInto . Pedoman paling penting di sini adalah bahwa konversi harus yang "jelas secara kanonik" - jika ada lebih dari satu cara yang masuk akal untuk mengonversi satu jenis ke jenis lainnya, TryFrom atau From mungkin tidak tepat hal-hal untuk digunakan.

_Secara umum_, menurut saya seharusnya tidak diharapkan bahwa semua jenis harus impl TryFrom<T> for T atau secara manual mengangkat impl From<U> for T . Secara khusus, sering kali tidak jelas jenis kesalahan mana yang harus dipilih. Namun, implementasi semacam itu sangat banyak yang dapat dan harus didefinisikan untuk membuat API tertentu berfungsi. Misalnya, kami memiliki kedua jenis implementasi ini untuk berbagai kombinasi tipe integer primitif untuk alasan yang diuraikan dalam RFC. Sebagai contoh lain, satu sifat ad-hoc yang akan diganti oleh TryFrom / TryInto adalah postgres::IntoConnectParams , yang memiliki implementasi refleksif untuk mengubah ConnectParams menjadi dirinya sendiri.

Secara umum, saya tidak berpikir bahwa semua jenis harus impl TryFrom<T> for T atau secara manual mengangkat impl From<U> for T .

Ketika konversi TryFrom kebetulan sempurna, jenis kesalahannya seharusnya enum Void {} , bukan? (Atau enum serupa dengan nama lain.) Omong-omong, bagi saya terdengar seperti alasan yang bagus untuk memiliki tujuan umum Void ketik std .

@SimonSapin yang akan merusak API gaya IntoConnectParams serta kasus penggunaan konversi bilangan bulat yang diuraikan dalam RFC karena jenis kesalahan dari konversi sempurna dan tidak dapat salah tidak akan cocok.

@sfackler Tidak jika Anda menggunakan From biasa untuk jenis kesalahan (misalnya try! )! impl<T> From<!> for T dapat dan harus ada untuk tujuan itu.

Sama seperti kita secara bertahap menambahkan implementasi From dan Into ke tipe perpustakaan standar, saya berharap kita akan melakukan hal yang sama untuk TryFrom dan TryInto .

Itu tampaknya belum terjadi, jadi saya tidak tahu apakah itu karena tidak ada yang mengetahuinya atau tidak ada tipe yang berlaku di std . Jika ada tipe yang berlaku di std , alangkah baiknya melihat beberapa implementasi untuk mereka. Misalnya, apakah ada implementasi bagus berikut ini?

impl TryFrom<u32> for char
impl TryFrom<char> for u8
impl TryFrom<Vec<u8>> for String
impl TryFrom<&[u8]> for &str

_Secara umum_, menurut saya seharusnya tidak diharapkan bahwa semua jenis harus impl TryFrom<T> for T atau secara manual mengangkat impl From<U> for T .

Masalahnya adalah jika Anda ingin menggunakan TryInto<T> dan T tidak ada di peti Anda, maka Anda hanya perlu berharap impl TryFrom<T> for T telah diterapkan. Itu adalah batasan yang disayangkan, batasan yang tidak ada untuk From / Into .

@SimonSapin yang akan merusak API gaya IntoConnectParams serta kasus penggunaan konversi bilangan bulat yang diuraikan dalam RFC karena jenis kesalahan dari konversi yang sempurna dan yang dapat salah tidak akan cocok.

Apakah ini berarti jenis kesalahan entah bagaimana diperbaiki berdasarkan jenis yang Anda konversi?

Mengapa TryFrom::Err dan TryInto::Err tidak dibatasi oleh std::error::Error ?

tidak dibatasi oleh std::error::Error?

Itu akan mencegah Err menjadi () , yang merupakan jenis kesalahan yang layak untuk konversi sempurna.

Jika () adalah tipe kesalahan, fungsi dapat mengembalikan Err(()) . Ini tidak membuat fungsinya sempurna. Itu hanya gagal memberikan kesalahan yang berguna ketika gagal. Saat menulis kode yang menggunakan konversi yang mungkin salah atau tidak, saya pikir cara terbaik adalah dengan mengikat Into dan menulis spesialisasi yang menggunakan TryInto .

Setelah RFC 1216 diimplementasikan , ! akan menjadi jenis kesalahan yang sesuai untuk konversi sempurna. Saya _berpikir_ itu berarti bahwa Error terikat pada TryFrom::Err / TryInto::Err akan baik-baik saja.

Saya pasti akan menyukainya jika mengimplementasikan From<T> menghasilkan implementasi TryFrom<T, Err = !> (dan juga untuk Into , tentu saja).

Saya _berpikir_ itu berarti bahwa Error terikat pada TryFrom::Err / TryInto::Err akan baik-baik saja.

Ya, kami pasti akan memiliki impl Error for ! { ... } untuk kasus seperti ini.

Saya agak khawatir tentang perbedaan jenis dalam kasus penggunaan konversi bilangan bulat, tetapi sepertinya kita mungkin harus berusaha menstabilkan ini sampai ! -as-a-type mendarat untuk memiliki kesempatan bereksperimen.

Dalam POV saya semua jenis terkait yang mewakili kesalahan harus dibatasi oleh Error . Sayangnya, kami telah meninggalkan satu jenis tanpa itu: FromStr::Err (Satu-satunya jenis publik lainnya adalah TryInto dan TryFrom , diperiksa dengan ack 'type Err;' dan ack 'type Error;' ). Jangan sampai ini lagi.

Dibahas baru-baru ini, tim libs memutuskan untuk menstabilkan sifat-sifat ini sampai tipe ! berhasil.

Saya kira sekarang ini tidak lagi diblokir, kan?

Menghapus nominasi karena @sfackler akan menyelidiki tipe ! dan sifat-sifat ini.

Apakah ini akan memiliki perubahan untuk menjadi stabil di masa mendatang?

Mengapa TryFrom::Err dan TryInto::Err tidak dibatasi oleh std::error::Error?

std::err::Error tidak ada di #![no_std] build. Juga, dalam banyak kasus tidak ada manfaat dalam menerapkan std::err::Error untuk jenis kesalahan meskipun tersedia. Jadi, saya lebih suka tidak memiliki ikatan seperti itu.

Saya telah bereksperimen dengan menerapkan ini sendiri di perpustakaan saya sendiri. Saya ingin mengulangi kekhawatiran yang diungkapkan oleh @SimonSapin di https://github.com/rust-lang/rfcs/pull/1542#issuecomment -206804137: try!(x.try_into()); membingungkan karena kata "coba" adalah menggunakan dua cara yang berbeda dalam pernyataan yang sama.

Saya mengerti bahwa banyak orang menganggap hal-hal seperti itu harus ditulis x.try_into()?; , namun saya adalah salah satu dari sejumlah besar orang (berdasarkan semua perdebatan) yang sangat memilih untuk tidak menggunakan sintaks ? karena ... semua alasan yang disebutkan dalam semua perdebatan.

Secara pribadi saya pikir kita masih harus mencoba menemukan beberapa pola yang tidak memerlukan awalan try_ pada namanya.

Saya tidak merasa sangat kuat tentang penamaan, tapi saya pribadi tidak bisa memikirkan sesuatu yang lebih baik.

Sudah menjadi semi-standar untuk menggunakan try_ untuk rasa fungsi yang mengembalikan Result .

namun saya salah satu dari sejumlah besar orang (berdasarkan semua perdebatan) yang sangat memilih untuk tidak menggunakan sintaks ? karena...semua alasan yang disebutkan dalam semua perdebatan.

Perdebatan itu sudah berakhir sekarang bukan? Maksud saya, saya bersimpati dengan sisi perdebatan itu: Saya tidak tahu mengapa orang mengatakan mereka menganggap try!() sangat mengganggu, ? kurang terlihat, mendorong penanganan kesalahan yang malas dan sepertinya pemborosan sintaks untuk sesuatu yang sudah memiliki makro yang sangat bagus (kecuali jika diperluas menjadi sesuatu yang jauh lebih umum di masa depan).

Tapi itu di masa lalu sekarang. ? stabil dan tidak akan hilang. Jadi sebaiknya kita semua beralih ke sana sehingga kita semua menggunakan hal yang sama dan kita bisa berhenti mengkhawatirkan konflik nama dengan try! .

Saya ingin mengulangi kekhawatiran yang diungkapkan oleh @SimonSapin di rust-lang/rfcs#1542 (komentar)

Karena saya disebutkan namanya, izinkan saya mengatakan bahwa saya tidak benar-benar memiliki kekhawatiran itu lagi. Pada saat saya membuat komentar ini, operator ? adalah proposal yang masa depannya tidak pasti, tetapi sekarang tetap ada.

Juga, saya pikir menstabilkan lebih cepat daripada yang terakhir lebih penting daripada putaran nama lain bikeshedding. Sudah berbulan-bulan sejak RFC diterima dan ini telah diterapkan #[unstable] .

Sudah menjadi semi-standar untuk menggunakan try_ untuk rasa fungsi yang mengembalikan Hasil.

Ini adalah hal yang menurut saya paling aneh tentang fitur ini. Sebagian besar fungsi yang saya tulis mengembalikan Result tetapi saya tidak pernah memberi nama salah satu fungsi tersebut dengan awalan try_ kecuali ketika mencoba bereksperimen dengan sifat ini.

Juga, saya belum menemukan keuntungan praktis untuk menulis ini:

impl TryInto<X> for Y {
    type Err = MyErrorType;

   fn try_into(self) -> Result<X, Self::Err> { ... }
}

Sebagai gantinya, saya selalu bisa menulis ini, apalagi overhead sintaksis:

    fn into_x(self) -> Result<X, MyErrorType> { ... }

Saya tidak pernah harus menulis kode generik yang diparameterisasi oleh TryInto atau TryFrom meskipun memiliki banyak konversi, jadi bentuk terakhir sudah cukup untuk semua penggunaan saya dalam tipe yang saya definisikan. Saya pikir memiliki parameter TryInto<...> atau TryFrom<...> sepertinya merupakan bentuk yang dipertanyakan.

Juga, saya menemukan bahwa penamaan jenis kesalahan terkait Err alih-alih Error rawan kesalahan karena saya selalu mengetik Error . Saya perhatikan bahwa kesalahan ini dibuat bahkan selama penyusunan RFC itu sendiri: https://github.com/rust-lang/rfcs/pull/1542#r60139383. Juga, kode yang menggunakan Result sudah menggunakan nama Err secara ekstensif karena merupakan konstruktor Result .

Ada proposal alternatif yang berfokus pada tipe integer secara khusus dan menggunakan terminologi seperti "melebarkan" dan "sempit", misalnya x = try!(x.narrow()); yang juga saya terapkan. Saya menemukan bahwa proposal sudah cukup untuk penggunaan saya atas fungsionalitas yang diusulkan di sini dalam penggunaan saya yang sebenarnya karena saya hanya melakukan konversi seperti itu pada tipe integer bawaan. Ini juga lebih ergonomis dan lebih jelas (IMO) untuk kasus penggunaan yang cukup.

Juga, saya belum menemukan keuntungan praktis untuk menulis ini ...

Saya agak setuju. Sifat ini adalah tempat sesuatu dapat digunakan untuk membuat hal lain tetapi terkadang proses itu bisa gagal - tetapi itu terdengar seperti hampir semua fungsi. Maksud saya, haruskah kita memiliki impls ini?:

impl TryInto<TcpStream> for SocketAddr {
    type Err = io::Error;
    fn try_into(self) -> Result<TcpStream, io::Error> {
        TcpStream::connect(self)
    }
}

impl<T> TryInto<MutexGuard<T>> for Mutex<T> {
    type Err = TryLockError<MutexGuard<T>>;
    fn try_into(self) -> Result<Mutex<T>, Self::Err> {
        self.try_lock()
    }
}

impl TryInto<process::Output> for process::Child {
    type Err = io::Error;
    fn try_into(self) -> Result<process::Output, io::Error> {
        self.wait_with_output()
    }
}

impl TryInto<String> for Vec<u8> {
    type Err = FromUtf8Error;
    fn try_into(self) -> Result<String, FromUtf8Error> {
        String::from_utf8(self)
    }
}

Sifat ini tampaknya hampir terlalu umum. Dalam semua contoh di atas, akan jauh lebih baik untuk secara eksplisit mengatakan apa yang sebenarnya Anda lakukan daripada menelepon try_into .

Saya pikir memiliki parameter TryInto<...> atau TryFrom<...> sepertinya merupakan bentuk yang dipertanyakan.

Juga setuju. Mengapa Anda tidak melakukan konversi dan menangani kesalahan sebelum meneruskan nilai ke fungsi?

std::err::Error tidak ada di #![no_std] build. Juga, dalam banyak kasus tidak ada manfaat dalam menerapkan std::err::Error untuk jenis kesalahan meskipun tersedia. Jadi, saya lebih suka tidak memiliki ikatan seperti itu.

Satu-satunya manfaat dari dibatasi oleh std::error::Error adalah bahwa itu bisa menjadi nilai kembalian dari cause() kesalahan lain. Saya benar-benar tidak tahu mengapa tidak ada core::error::Error , tetapi saya belum memeriksanya.

Juga, saya menemukan bahwa menamai jenis kesalahan terkait Err alih-alih Kesalahan rawan kesalahan karena saya selalu mengetik Kesalahan. Saya perhatikan bahwa kesalahan ini dibuat bahkan selama penyusunan RFC itu sendiri: rust-lang/rfcs#1542 (komentar). Juga, kode yang menggunakan Result sudah menggunakan nama Err secara ekstensif karena merupakan konstruktor Result.

FromStr , yang stabil, juga menggunakan Err untuk tipe yang terkait. Apakah itu nama terbaik atau tidak, saya pikir penting untuk tetap konsisten di seluruh perpustakaan standar.

Apakah TryFrom dan TryInto terlalu umum atau tidak, saya benar-benar ingin melihat konversi yang salah, setidaknya di antara tipe integer, di pustaka standar. Saya memiliki peti untuk ini, tetapi saya pikir kasus penggunaan cukup jauh untuk menjadikannya standar. Kembali ketika Rust masih alfa atau beta, saya ingat menggunakan FromPrimitive dan ToPrimitive untuk tujuan ini, tetapi sifat-sifat itu memiliki masalah yang lebih besar.

Error tidak dapat dipindahkan ke core karena masalah koherensi.

Juga karena masalah koherensi Box<Error> tidak mengimplementasikan Error , alasan lain kami tidak mengikat tipe Err .

Sebenarnya tidak ada kebutuhan untuk mengikatnya pada definisi sifat, bagaimanapun juga - Anda selalu dapat menambahkan ikatan itu sendiri nanti:

where T: TryInto<Foo>, T::Err: Error

Saya biasanya tidak mengomentari utas ini, tetapi saya sudah menunggu ini untuk sementara waktu dan seperti yang diungkapkan oleh beberapa di atas, saya tidak yakin apa penangguhannya; Saya selalu ingin dari sifat yang bisa gagal. Saya memiliki kode yang disebut try_from ... Sifat ini _perfectly_ merangkum ide itu. Tolong izinkan saya menggunakannya pada karat yang stabil.

Saya juga menulis banyak hal lain, tetapi sejak itu saya telah menghapusnya karena sayangnya, koherensi sifat mencegah sifat ini menjadi berguna bagi saya. Misalnya, saya pada dasarnya telah mengimplementasikan ulang versi khusus dari sifat yang sama persis ini untuk tipe primitif untuk pengurai yang sangat umum dari tipe tersebut. Jangan khawatir, saya akan mengoceh tentang ini lain kali.

Karena itu, saya percaya str::parse akan mendapat banyak manfaat dari ini, karena ia juga mengkhususkan sifat FromStr sebagai ikatan - yang persis ( TryFrom<str> ) khusus tangan.

Jadi perbaiki saya jika saya salah, tetapi saya yakin menstabilkan dan menggunakan ini untuk str::parse akan:

  1. hapus implementasi ulang boilerplate, dan karenanya hapus sifat yang kurang akrab dan khusus
  2. tambahkan TryFrom<str> di tanda tangan, yang dengan benar ada di modul konversi dan lebih jelas apa fungsinya
  3. akan memungkinkan klien upstream untuk mengimplementasikan TryFrom<str> untuk tipe data mereka dan mendapatkan str.parse::<YourSweetDataType>() gratis, bersama dengan try_from lainnya yang mereka rasa ingin mereka implementasikan, yang membuat organisasi kode logis lebih baik.

Terakhir, selain abstraksi, penggunaan kembali kode, bla bla, saya pikir salah satu manfaat bersahaja dari sifat seperti ini adalah pembelian semantik yang mereka berikan untuk pemula dan veteran. Pada dasarnya mereka menyediakan (atau mulai menyediakan, semakin kami menstabilkan) lanskap yang seragam dengan perilaku kanonik yang familiar. Default , From , Clone adalah contoh yang sangat bagus untuk ini. Mereka menyediakan lanskap fungsi yang mudah diingat yang dapat dijangkau pengguna saat melakukan operasi tertentu, dan yang perilaku dan semantiknya telah mereka pahami dengan baik (belajar sekali, terapkan di mana saja). Misalnya:

  1. Saya ingin versi default; oh biarkan aku meraih SomeType::default()
  2. Saya ingin mengonversi ini, saya ingin tahu apakah SomeType::from(other) diimplementasikan (Anda bisa mengetiknya dan melihat apakah itu dikompilasi, tanpa meraih dokumentasi)
  3. Saya ingin mengkloning ini, clone()
  4. Saya ingin mencoba mendapatkan ini dari ini, dan karena kesalahan adalah bagian integral dari karat, itu bisa gagal dalam tanda tangan, jadi izinkan saya try_from - oh tunggu: P

Semua metode ini menjadi biasa dan menjadi bagian dari toolkit pengguna Rust, yang secara imho mengurangi beban logis dengan memungkinkan kita menjangkau konsep yang sudah dikenal (dan itu hanya nama lain untuk Sifat!) yang dokumentasi dan perilaku semantiknya telah kita internalisasikan.

Tentu saja mereka tidak selalu cocok, dalam hal ini kami mengkhususkan struktur data kami, tetapi sifat seperti ini mengurangi beban API pengguna dan pembuat kode dengan memberi mereka akses ke konsep yang telah mereka pelajari, alih-alih membaca dokumentasi/menemukan Anda from_some_thing , dll. Tanpa harus membaca dokumentasi Anda, dengan menggunakan sifat std, pengguna dapat menavigasi api dan struktur data Anda dengan cara yang logis, kuat, dan efisien.

Dalam beberapa hal, ini meresmikan kesepakatan orang yang baik di antara kita sendiri, dan membuatnya lebih mudah dan lebih akrab bagi kita untuk melakukan operasi tertentu yang sudah dikenal.

Dan itu saja yang harus saya katakan tentang itu;)

Ini sebelumnya diblokir pada penyelidikan kemungkinan menggunakan ! sebagai jenis kesalahan untuk konversi bilangan bulat sempurna. Karena fitur tersebut saat ini diterapkan, ini gagal bahkan dalam kasus yang paling sederhana: https://is.gd/Ws3K7V.

Apakah kita masih berpikir untuk mengubah nama metode, atau haruskah kita memasukkan fitur ini ke dalam FCP?

@sfackler Tautan taman bermain itu berfungsi untuk saya jika saya mengubah tipe pengembalian pada baris 29 dari Result<u32, ()> menjadi Result<u32, !> : https://is.gd/A9pWbU Itu gagal untuk mengenali bahwa let Ok(x) = val; adalah pola yang tidak dapat disangkal ketika val memiliki tipe Err !, tetapi itu sepertinya bukan masalah pemblokiran.

@Ixrec motivasi utama untuk sifat-sifat ini adalah konversi ke dan dari C integer typedefs. Jika saya memiliki fungsi

fn foo(x: i64) -> Result<c_long, TryFromIntError> {
    x.try_into()
}

Ini akan dikompilasi pada target i686 tetapi tidak pada target x86_64.

Demikian pula, katakanlah saya ingin mengganti tipe IntoConnectParams : https://docs.rs/postgres/0.13.4/postgres/params/trait.IntoConnectParams.html. Bagaimana saya bisa melakukannya jika ada selimut impl<T> TryFrom<T> for T { type Error = ! } ? Saya memerlukan implementasi refleksif untuk ConnectParams , tetapi dengan jenis kesalahan konkret yang berbeda dari ! .

Itu gagal untuk mengenali bahwa let Ok(x) = val; adalah pola yang tak terbantahkan ketika val memiliki tipe Err !

Perhatikan bahwa ada PR terbuka untuk itu .

Jika saya memiliki fungsi ...

Ini seharusnya berhasil

fn foo(x: i64) -> Result<c_long, TryFromIntError> {
    let val = x.try_into()?;
    Ok(val)
}

Dengan risiko menjadi komentar +1 yang mengganggu, saya hanya ingin menyebutkan bahwa setelah makro 1.1 tiba di Rust 1.15, try_from akan menjadi fitur terakhir yang menjaga Ruma di Rust setiap malam. Try_from yang stabil sangat ditunggu-tunggu!

Pada catatan yang lebih substansial ...

Ini adalah hal yang menurut saya paling aneh tentang fitur ini. Sebagian besar fungsi yang saya tulis mengembalikan Hasil tetapi saya tidak pernah menamai salah satu fungsi tersebut dengan awalan try_ kecuali ketika mencoba bereksperimen dengan sifat ini.

Itu pengamatan yang bagus, tetapi saya pikir alasan untuk awalan try_ bukanlah karena perlu mengidentifikasi tipe pengembalian sebagai Result , tetapi untuk membedakannya dari padanan yang tidak dapat salah.

Juga, saya menemukan bahwa menamai jenis kesalahan terkait Err alih-alih Kesalahan rawan kesalahan karena saya selalu mengetik Kesalahan. Saya perhatikan bahwa kesalahan ini dibuat bahkan selama penyusunan RFC itu sendiri: rust-lang/rfcs#1542 (komentar). Juga, kode yang menggunakan Result sudah menggunakan nama Err secara ekstensif karena merupakan konstruktor Result.

Saya setuju dengan yang satu ini. Sebagian besar jenis kesalahan lain yang saya temui di perpustakaan bernama "Kesalahan" dan saya suka sejauh ini "Err" hanya berarti Result::Err . Sepertinya menstabilkannya karena "Err" akan menghasilkan (tidak ada permainan kata-kata) pada orang yang terus-menerus salah menyebut nama.

@canndrew Tentu saja mungkin untuk membuatnya bekerja, tetapi inti dari motivasi untuk fitur ini adalah untuk memudahkan menangani perbedaan platform semacam ini dengan benar - kami ingin menghindari seluruh ruang "kompilasi pada x86 tetapi bukan ARM" .

Saya pikir saya memilih Err untuk konsistensi dengan FromStr tetapi saya akan sangat senang untuk beralih ke Error sebelum menstabilkan!

fitur terakhir menjaga Ruma di malam hari Rust

Demikian juga, backend taman bermain alternatif hanya perlu setiap malam untuk akses ke TryFrom ; hal-hal serde malam terlalu tidak stabil untuk keinginan saya, jadi saya pindah ke pengaturan skrip build. Dengan 1,15, saya akan kembali ke #[derive] . Saya tidak sabar menunggu fitur ini menjadi stabil!

@sfackler Maaf saya tidak mengikuti. Dalam kasus konversi bilangan bulat, sepertinya yang benar-benar kita butuhkan adalah tidak mengetik c_ulong menjadi u32 atau u64 tergantung pada platform tetapi entah bagaimana membuatnya menjadi tipe baru. Dengan begitu impl TryFrom<c_ulong> tidak dapat mengganggu impl TryFrom<u{32,64}> .
Lagi pula, kita akan selalu memiliki masalah "mengkompilasi satu platform tetapi tidak yang lain" jika kita mendefinisikan tipe secara berbeda pada platform yang berbeda. Sayang sekali harus mengorbankan impl TryFrom<T> for U where U: From<T> yang sebaliknya-sepenuhnya-logis hanya agar kami dapat mendukung apa yang tampaknya merupakan praktik yang meragukan.

Saya tidak secara serius menyarankan agar kita memblokir RFC ini sampai kita mendapatkan RFC tipe baru integer yang ditulis + digabungkan + distabilkan. Tapi kita harus mengingatnya untuk masa depan.

Demikian pula, katakanlah saya ingin mengganti tipe IntoConnectParams:

Apa masalahnya di sini? Mengapa Anda tidak menggunakan satu jenis kesalahan untuk TryFrom<ConnectParams> dan yang lain untuk TryFrom<&'a str> ?

Saya tidak akan menganjurkan bahwa kita benar-benar melanggar semua kode FFI di dunia. Setelah mencoba dan gagal untuk mengambil pembungkus tipe baru integer serupa seperti Wrapping , ada biaya ergonomis yang sangat besar.

Apakah ada impl<T> From<!> for T di perpustakaan standar? Saya tidak melihatnya di dokumen tetapi itu mungkin hanya bug rustdoc. Jika tidak ada, maka tidak ada cara untuk mengubah TryFrom<ConnectParams> impl ! Error menjadi yang benar-benar saya butuhkan.

Setelah mencoba dan gagal untuk mengambil pembungkus tipe baru integer yang serupa seperti Pembungkusan, ada biaya ergonomis yang sangat besar.

Saya sedang memikirkan sesuatu yang lebih seperti dapat mendefinisikan tipe integer Anda sendiri a. la. C++:

trait IntLiteral: Integer {
    const SUFFIX: &'static str;
    const fn from_bytes(is_negative: bool, bytes: &[u8]) -> Option<Self>; // or whatever
}

impl IntLiteral for c_ulong {
    const SUFFIX: &'static str = "c_ulong";
    ...
}

extern fn foo(x: c_ulong);

foo(123c_ulong); // use a c_ulong literal
foo(123); // infer the type of the integer

Apakah itu akan menyelesaikan sebagian besar masalah ergonomis? Saya sebenarnya tidak suka literal yang ditentukan pengguna C++ - atau fitur secara umum yang memberi orang kekuatan untuk mengubah bahasa dengan cara yang membingungkan - tetapi saya kira itu bisa menjadi yang lebih rendah dari dua kejahatan.

Apakah ada impl<T> From<!> for T di perpustakaan standar?

Tidak saat ini karena bertentangan dengan From<T> for T impl. Pemahaman saya adalah bahwa spesialisasi impl pada akhirnya harus dapat menangani ini.

Kedengarannya seperti sesuatu yang harus kita putar kembali dalam beberapa tahun.

Apa timeline stabilisasi spesialisasi dan ! ?

Untuk spesialisasi saya tidak tahu. Untuk ! itu sendiri sebagian besar masalah kapan saya bisa menggabungkan tambalan saya.

@canndrew Saya sangat setuju bahwa itu tidak boleh diterapkan untuk semuanya. Dokumen mengatakan _attempt untuk membuat Self melalui konversi_, tetapi apa yang dianggap sebagai konversi? Bagaimana dengan…_mengubah hal yang sama dari satu representasi ke representasi lainnya, atau menambahkan atau menghapus pembungkus_? Ini mencakup Vec<u8> -> String dan Mutex<T> -> MutexGuard<T> Anda, serta hal-hal seperti u32 -> char dan &str -> i64 ; sementara mengecualikan SocketAddr -> TcpStream dan process::Child -> process::Output .

Saya merasa impl From<T> for U mungkin menyiratkan TryFrom<T, Err=!> for U . Tanpa itu, fungsi yang membutuhkan TryFrom<T> s juga tidak dapat mengambil From<T> s. (Menggunakan try_from() | try_into() untuk konversi yang konkrit dan sempurna mungkin hanya akan menjadi anti-pola. Anda _akan_ dapat melakukannya, tapi…itu konyol, jadi jangan .)

Saya merasa seperti impl Dariuntuk U mungkin harus menyiratkan TryFromuntukmu.

Sepakat.

Anda akan bisa melakukannya, tapi… itu akan konyol, jadi jangan lakukan.

Kedengarannya seperti serat clippy potensial.

@BlacklightShining Saya pikir itu harus diterapkan untuk tipe di mana, mengingat tipe output, "konversi" sudah jelas. Segera setelah beberapa konversi dimungkinkan (utf8/16/32? serialisasi vs casting? dll...), itu harus dihindari.

MENURUT OPINI SAYA:

Saat ini, kami dapat dengan mudah menyediakan implementasi selimut TryFrom<&str> untuk semua yang mengimplementasikan FromStr :

impl<'a, T: FromStr> TryFrom<&'a str> for T {
    type Err = <T as FromStr>::Err;
    fn try_from(s: &'a s) -> Result<T, Self::Err> {
        T::from_str(s)
    }
}

Saya ingin melihat sesuatu seperti ini ditambahkan sebelum try_from distabilkan, atau setidaknya beberapa referensi untuk itu di dokumen. Jika tidak, mungkin akan membingungkan memiliki implementasi TryFrom<&'a str> dan FromStr yang berbeda.

Saya setuju bahwa itu akan menjadi penyertaan yang baik, tetapi mungkin bertentangan dengan Try -> TryFrom impl yang diusulkan?

@sfackler berpotensi. Akan baik-baik saja jika TryFrom , TryInto , dan FromStr semuanya saling mereferensikan dalam dokumen, tetapi perhatian utama saya adalah tidak ada standar yang mengatakan apakah str::parse dan str::try_into mengembalikan nilai yang sama.

Saya akan baik-baik saja dengan permintaan lunak dalam dokumen bahwa orang harus mengimplementasikannya untuk memiliki perilaku yang sama, meskipun saya pasti dapat melihat kasus di mana seseorang mungkin berpikir bahwa mereka bisa berbeda.

Sebagai contoh, katakanlah seseorang membuat struct Password untuk sebuah situs web. Mereka mungkin berasumsi bahwa "password".parse() akan memeriksa validitas kata sandi, kemudian mengubahnya menjadi hash, sedangkan Password::try_from("1234abcd") mungkin berasumsi bahwa "1234abcd" sudah menjadi hash yang disimpan dalam database dan mencoba untuk menguraikannya langsung menjadi hash yang dapat dibandingkan.

Ini masuk akal, mengingat bagaimana susunan kata dari parse menyiratkan bahwa beberapa tingkat penguraian telah dilakukan, sedangkan try_from hanyalah sebuah konversi tipe. Namun, pada kenyataannya, kami mungkin ingin mengklarifikasi bahwa kedua fungsi ini bermaksud melakukan hal yang sama.

Meskipun tim bahasa telah mengusulkan untuk menutup RFC karena tidak lagi menggunakan parameter anonim , mereka semua tampaknya setuju bahwa idealnya kita akan berhenti membuat kode baru yang menggunakan parameter anonim. Dengan mengingat hal itu, dapatkah kita memperbarui tanda tangan try_from / try_into untuk memberikan nama parameter? Atau akan lebih penting untuk mempertahankan simetri dengan from / into ?

Juga, apakah berguna untuk menulis ringkasan pertanyaan utama yang belum terjawab yang masih tersisa? Saya sangat berharap kami dapat memutuskan untuk menstabilkan ini untuk siklus rilis berikutnya. Seperti yang saya sebutkan, itu satu-satunya fitur malam yang tersisa yang sering saya gunakan. :}

@jimmycuadra pasti ya! Ingin mengirim PR menambahkan beberapa nama parameter?

Sehubungan dengan keadaan saat ini, saya percaya satu-satunya pertanyaan yang belum terjawab adalah apakah harus ada

impl<T, U> TryFrom<U> for T
    where T: From<U>
{
    type Error = !;

    fn try_from(u: U) -> Result<T, !> {
        Ok(T::from(u))
    }
}

Ini menambahkan beberapa jumlah simetri yang bagus, tetapi membuat hal-hal sedikit lebih menjengkelkan untuk ditangani dalam banyak kasus karena Anda tidak lagi memiliki satu jenis kesalahan untuk hal-hal yang ingin Anda konversi.

ketik Kesalahan = !;

Saya sarankan melakukannya dalam RFC terpisah yang memperhitungkan hasil dari apa pun yang diputuskan tentang semua hal yang belum diputuskan mengenai ! .

@sfackler Saya pikir penting untuk mempertimbangkan hal-hal yang saya sebutkan tentang FromStr juga. Haruskah kita memiliki impl serupa untuk pelaksana FromStr , atau haruskah mereka diizinkan untuk menjadi berbeda, atau haruskah kita hanya mendokumentasikan bahwa mereka harus sama tetapi tidak harus?

Saya berpendapat bahwa TryFrom<str> dan FromStr harus identik secara fungsional, dan dokumentasi harus memperjelas bahwa implementasi keduanya dimaksudkan untuk identik. Menerapkan satu juga harus memberi Anda yang lain, setidaknya dalam hal memungkinkan Anda untuk menggunakan str::parse . Jika Rust memiliki TryFrom sejak awal, FromStr tidak akan pernah dibutuhkan. Untuk alasan itu, saya juga akan mendokumentasikan TryFrom<str> sebagai bentuk pilihan untuk kode baru.

@jimmycuadra dalam hal ini, kita harus memodifikasi parse untuk menggunakan TryFrom dan kemudian menempatkan blanket impl untuk FromStr -> TryFrom .

Jika kita akan mengubah str::parse untuk diimplementasikan dalam bentuk TryFrom , apakah kita juga harus mengubah implementasi lain dari FromStr untuk tipe konkret yang serupa (yaitu semua pelaksana dalam daftar ini : https://doc.rust-lang.org/stable/std/str/trait.FromStr.html)? Haruskah dokumen untuk FromStr diperbarui untuk menyarankan penggunaan TryFrom sebagai gantinya? Haruskah implementasi konkret saat ini dari FromStr memindahkan dokumen mereka ke versi TryFrom ?

Saya pikir untuk kompatibilitas mundur kami tidak dapat mengubah FromStr , bukan?

Menghapus FromStr demi TryFrom<&str> jelas merupakan sesuatu yang perlu diingat untuk Rust 2.0.

Ya, setelah fitur ini stabil, kami akan mengajukan masalah dan menandainya dengan 2.0-breakage-wishlist.

Satu hal yang juga perlu dipertimbangkan adalah menambahkan metode parse_into ke String yang menggunakan TryFrom<String> . Saya mendapati diri saya sering mengimplementasikan TryFrom untuk keduanya jika suatu tipe secara internal menyimpan String tetapi masih memerlukan validasi.

Jika kita akan meninggalkan implCobaDari untuk T di mana T: Dari untuk RFC masa depan, apakah fitur ini siap distabilkan sekarang? Saya benar-benar tidak ingin melewatkan siklus rilis lain, jadi saya berharap beberapa orang di tim Rust memiliki bandwidth untuk berdiskusi dan membuat keputusan.

Saya pikir masalahnya adalah akan sulit untuk menstabilkan fitur itu setelah distabilkan dan orang-orang telah memberikan impls untuk keduanya.

Saya berharap bahwa sudah memiliki T : From<U> akan menempatkan U : TryFrom<T> dalam kategori perubahan " lubang API yang jelas " ketika implementasinya masuk akal.

Itu menyiratkan setidaknya harus ada T : TryFrom<T> dengan Error = ! , tetapi versi untuk From yang sempurna jelas lebih baik dari itu.

IMO tidak ada perbedaan yang jelas antara apakah From harus menyediakan implementasi TryFrom atau apakah TryFrom harus menyediakan implementasi From .

Karena di satu sisi, Anda dapat menganggap T::from(val) hanya T::try_from(val).unwrap() , dan di sisi lain, Anda dapat menganggap T::try_from(val) hanya Ok(T::from(val)) . Mana yang lebih baik? Saya tidak tahu.

Anda dapat menganggap T::from(val) hanya T::try_from(val).unwrap()

Saya tidak setuju dengan yang itu. Implementasi From diperkirakan tidak akan membuat panik. Hanya sebaliknya yang masuk akal.

@clarcharr Karena Dari tidak perlu panik, opsinya adalah From dalam hal TryFrom<Error=!> atau sebaliknya. Tapi saya tidak suka saran yang biasa menjadi "Anda harus menerapkan TryFrom dengan type Error = ! " alih-alih "Anda harus menerapkan From ".

Adakah cara untuk mendapatkan gerakan untuk menstabilkan ini? Kami kehabisan waktu sebelum 1,18 masuk ke beta. @sfackler?

@rfcbot menggabungkan fcp

Anggota tim @sfackler telah mengusulkan untuk menggabungkan ini. Langkah selanjutnya adalah peninjauan oleh sisa tim yang ditandai:

  • [x] @BurntSushi
  • [x] @Kimundi
  • [x] @alexcrichton
  • [x] @aturon
  • [x] @brson
  • [x] @sfackler

Tidak ada kekhawatiran saat ini terdaftar.

Setelah peninjau ini mencapai konsensus, ini akan memasuki periode komentar terakhirnya. Jika Anda menemukan masalah besar yang belum diangkat pada titik mana pun dalam proses ini, silakan angkat bicara!

Lihat dokumen ini untuk info tentang perintah apa yang dapat diberikan oleh anggota tim yang ditandai kepada saya.

@sfackler : hanya untuk check-in, apakah kita baik dalam berbagai masalah sekitar ! dan blanket impls? Saya tahu kita membicarakan hal ini dalam pertemuan libs, tetapi akan sangat membantu untuk mendapatkan ringkasannya di sini.

@aturon Diskusi terakhir tentang itu adalah @sfackler bertanya- tanya apakah impl From<T> for U harus menyediakan impl TryFrom<T> for U where TryFrom::Error = ! .

@briansmith menyarankan agar keputusan tentang itu menjadi RFC terpisah setelah pertanyaan yang belum terselesaikan seputar tipe yang tidak pernah diselesaikan.

Bukankah masalah utama dengan menstabilkan ini sekarang adalah bahwa perubahan seperti itu tidak dapat dilakukan tanpa merusak kompatibilitas ke belakang? Atau apakah solusi untuk tidak bergerak maju dengan perubahan itu?

Saya pikir rangkaian impls saat ini tidak dapat dipertahankan. Saya dapat memahami salah satu dari posisi ini:

  1. TryFrom dimaksudkan untuk konversi yang salah _only_, jadi tidak memiliki hal-hal seperti u8 -> u128 atau usize -> usize .
  2. TryFrom ditujukan untuk _semua_ konversi, beberapa di antaranya tidak dapat salah dan karenanya memiliki tipe TryFrom::Error yang tidak berpenghuni.

Tetapi saat ini semuanya berada dalam keadaan hibrida yang aneh di mana kompiler akan memasukkan kode pemeriksaan untuk konversi i32 -> i32 namun Anda tidak dapat melakukan konversi String -> String .

Apa keberatan ! sebagai jenis Kesalahan? Satu-satunya hal yang saya perhatikan dalam skim cepat adalah "tetapi membuat hal-hal sedikit lebih menjengkelkan untuk ditangani dalam banyak kasus karena Anda tidak lagi memiliki satu jenis kesalahan untuk hal-hal yang ingin Anda konversi", tetapi saya tidak yakin saya setuju dengan itu karena Anda harus menganggap Anda telah melewati sesuatu yang khusus dengan jenis kesalahan khusus dalam konteks umum apa pun yang terjadi.

Bukankah masalah utama dengan menstabilkan ini sekarang adalah bahwa perubahan seperti itu tidak dapat dilakukan tanpa merusak kompatibilitas ke belakang? Atau apakah solusi untuk tidak bergerak maju dengan perubahan itu?

Saya berpendapat bahwa terlalu bersemangat untuk menambahkan implementasi generik TryFrom ketika From diimplementasikan. Meskipun secara semantik benar bahwa jika ada implementasi From , ada implementasi TryFrom yang tidak dapat menghasilkan kesalahan, saya tidak melihat implementasi yang disediakan ini praktis berguna sama sekali, apalagi a kebutuhan yang cukup umum sehingga harus disediakan secara default. Jika seseorang benar-benar membutuhkan perilaku itu untuk tipe mereka karena suatu alasan, itu hanya satu implementasi sederhana.

Jika ada contoh kasus ketika Anda pernah menggunakan try_from alih-alih from untuk konversi yang sempurna, saya pasti bisa berubah pikiran.

Apa keberatannya! sebagai jenis Kesalahan?

! tinggal beberapa bulan lagi dari stabilisasi. Pilih salah satu dari menstabilkan TryFrom dalam waktu dekat atau memiliki impl<T, U> TryFrom<U> for T where T: From<U> .

Sudahkah kita mempertimbangkan untuk menggunakan alias sifat (rust-lang/rfcs#1733) di sini? Saat itu mendarat, kita bisa alias From<T> menjadi TryFrom<T, Error=!> , membuat dua sifat menjadi satu dan sama.

@lfairy Itu akan merusak pengguna impl s dari From , sayangnya.

@glaebhoerl Ya, Anda benar Bagian motivasi dari RFC menyebutkan aliasing impl s tetapi proposal sebenarnya tidak mengizinkannya.

(Bahkan jika tidak, metodenya memiliki nama yang berbeda, dll.)

Itu bisa berada di bawah daftar harapan 2.0 tetapi terlepas dari itu, itu tidak akan terjadi tanpa merusak apa pun.

Pertama-tama, terima kasih @sfackler untuk percakapan hebat tentang ini di IRC. Setelah membiarkan hal-hal duduk di kepala saya sebentar, di sinilah saya berakhir.

Pilih salah satu dari menstabilkan TryFrom dalam waktu dekat atau memiliki impl<T, U> TryFrom<U> for T where T: From<U> .

Saya pikir pertanyaan inti di sini adalah apakah pertobatan yang sempurna termasuk dalam sifat itu. Saya pikir mereka melakukannya, untuk hal-hal seperti konversi sempurna di RFC dan untuk penggunaan umum (analog dengan bagaimana ada T:From<T> yang tampaknya tidak berguna). Mengingat itu, yang paling saya inginkan adalah menghindari dunia di mana setiap pelaksana tipe diharapkan impl TryFrom<MyType> for MyType , dan setiap From impl juga harus menghasilkan TryFrom impl. (Atau dapatkan bug yang diajukan nanti karena tidak menyediakannya.)

Jadi bisakah kita melakukan blanket impl tanpa menstabilkan ! ? Saya pikir ada caranya, karena kita sudah memiliki ! -jenis seperti di perpustakaan, seperti std::string::ParseError . (" Enum ini sedikit canggung: tidak akan pernah benar-benar ada. ")

Sebuah sketsa bagaimana ini bisa bekerja:

  • Jenis baru, core::convert::Infallible , diimplementasikan persis seperti std::string::ParseError . (Bahkan mungkin mengubah yang terakhir ke alias tipe untuk yang pertama.)
  • impl<T> From<Infallible> for T sehingga kompatibel di ? dengan jenis kesalahan apa pun (lihat item c_foo nanti)
  • Gunakan Infallible sebagai Error ketik di blanket impl
  • Kemudian, pertimbangkan type Infallible = !; sebagai bagian dari menstabilkan tipe never

Saya akan secara sukarela membuat PR melakukan itu, jika itu akan membantu untuk mengkonkretkannya.

Adapun c_foo : Di atas akan terus memungkinkan kode seperti ini:

fn foo(x: c_int) -> Result<i32, TryFromIntError> { Ok(x.try_into()?) }

Tapi itu akan membuat kode seperti ini menjadi "kaki" portabilitas, karena jenis kesalahan yang berbeda

fn foo(x: c_int) -> Result<i32, TryFromIntError> { x.try_into() }

Secara pribadi, saya tidak peduli dengan perbedaan itu karena selama c_int adalah alias tipe, ada footgun "otomatis penuh":

fn foo(x: c_int) -> i32 { x }

Dan secara umum, kode yang mengharapkan tipe terkait pada suatu sifat menjadi sama untuk impls yang berbeda terasa seperti bau kode bagi saya. Saya membaca TryFrom sebagai "generalisasi From "; jika tujuannya adalah "konversi yang lebih baik antara himpunan bagian bilangan bulat"--yang juga terasa berguna--maka "jenis kesalahan yang selalu sama" adalah logis, tetapi saya mengharapkan sesuatu yang ditargetkan dalam std::num sebagai gantinya, mungkin seperti num::cast::NumCast (atau boost::numeric_cast ).

(Selain: dengan #[repr(transparent)] dalam penggabungan FCP, mungkin tipe c_foo dapat menjadi tipe baru, di mana konversi ini bisa lebih konsisten. Implikasi From&TryFrom dapat mengkodifikasi C "char <= short < = int <= long", serta ukuran minimum yang diperlukan standar untuk aturan tersebut seperti c_int:From<i16> atau c_long:TryFrom<i64> . Maka konversi di atas akan menjadi i32:TryFrom<c_int> pada semua platform, dengan tipe Error yang selalu sama, dan masalahnya hilang.)

Mengenai "Enum ini sedikit canggung: itu tidak akan pernah benar-benar ada."

Apakah ada alasan mengapa jenis kesalahan tidak bisa hanya berupa unit? Mengapa repot-repot dengan struct ParseError yang unik jika percakapan tidak pernah bisa error?

@sunjay () adalah representasi sistem tipe dari "ini bisa terjadi, tetapi tidak ada yang menarik untuk diberitahukan kepada Anda ketika itu terjadi". Tipe yang tidak berpenghuni (seperti ! dan std::string::ParseError ) adalah kebalikannya, cara sistem tipe mengatakan "situasi ini tidak akan pernah terjadi, jadi Anda tidak perlu menghadapinya."

@jimmycuadra

Jika ada contoh kasus ketika Anda pernah menggunakan try_from alih-alih dari untuk konversi yang sempurna, saya pasti bisa berubah pikiran.

@scottmcm

Saya pikir pertanyaan inti di sini adalah apakah pertobatan yang sempurna termasuk dalam sifat itu.

Inilah kasus penggunaan saya: Saya memiliki format file konfigurasi di mana nilainya dapat berupa bool, numerik, atau string, dan makro untuk menulis nilai konfigurasi literal di mana kuncinya dapat berupa varian enum atau string. Sebagai contoh:

let cfg = config![
    BoolOpt::SomeCfgKey => true,
    "SomeOtherCfgKey" => 77,
];

Singkat cerita, makro akhirnya meluas ke daftar panggilan ($k, $v).into() . Saya ingin memeriksa konversi untuk kunci string untuk memastikan mereka memberi nama opsi konfigurasi yang valid, yaitu menerapkan TryFrom<(String, ???)> dan mengubah makro untuk menggunakan ($k, $v).try_into() . Akan lebih sulit untuk melakukan semua ini jika tidak ada nama metode tunggal yang digunakan makro untuk semua konversi.

:bell: Ini sekarang memasuki periode komentar terakhir , sesuai ulasan di atas . :lonceng:

Saya sebenarnya sangat menyukai gagasan tentang:

impl<U: TryFrom<T, Error=!>> From<T> for U {
    fn from(val: T) -> U {
        val.unwrap()
    }
}

Karena siapa pun yang menginginkan TryFrom<Error=!> dapat mengimplementasikannya, tetapi orang masih dapat mengimplementasikan From jika mereka mau. Mungkin pada akhirnya kita bisa mencela From tetapi kita tidak harus melakukannya.

Rencana @scottmcm untuk menggunakan enum kosong terdengar bagus bagi saya.

@ Ericson2314 Anda menulis :

Tidak jika Anda menggunakan From biasa untuk jenis kesalahan (misalnya try! )! impl<T> From<!> for T dapat dan harus ada untuk tujuan itu.

Bagaimana ini akan berhasil dalam praktik? Katakanlah saya mencoba menulis fungsi seperti ini:

fn myfn<P: TryInto<MyType>>(p: P) -> Result<(), MyError>

Kecuali ini tentu saja tidak berhasil, saya perlu menentukan Error= pada TryInto . Tapi jenis apa yang harus saya tulis di sana? MyError tampak jelas tetapi kemudian saya tidak dapat menggunakan MyType dengan blanket TryFrom impl.

Apakah Anda menyarankan hal berikut?

fn myfn<E: Into<MyError>, P: TryInto<MyType, Error=E>>(p: P) -> Result<(), MyError>

Itu tampaknya cukup bertele-tele.

Ada pertanyaan yang lebih umum tentang bagaimana ini seharusnya bekerja jika saya ingin beberapa konversi "sempurna" untuk jenis yang sama tetapi dengan jenis kesalahan yang berbeda.

Mungkin definisi TryFrom harus diubah menjadi berikut:

pub trait TryFrom<T, E>: Sized {
    type Error: Into<E>;

    fn try_from(t: T) -> Result<Self, E>;
}

Anda dapat membatasi kesalahan agar dapat dikonversi menjadi MyError tanpa harus memberikan nama eksplisit, seperti

fn myfn<P: TryInto<MyType>>(p: P) -> Result<(), MyError> where MyError: From<P::Error>

itu masih sedikit bertele-tele, tetapi benar-benar menyatakan batasan untuk memanggil fungsi dengan baik ( playground )

EDIT: Dan mencoba dengan varian seperti P::Error: Into<MyError> sebenarnya tidak berfungsi dengan ? karena tidak ada implementasi selimut From untuk Into . Mengubah TryFrom seperti yang Anda tunjukkan, saya harapkan akan mengalami masalah yang sama.

Periode komentar terakhir sekarang selesai.

@jethrogb heh Anda mengutip saya dari setahun yang lalu jadi saya harus berpikir sebentar. @Nemo157 benar sekali dan itu tampaknya masuk akal.

Dalam kasus khusus ! kita harus memiliki blanket impl, tapi saya ingat itu tumpang tindih dengan yang lain. Ini tumpang tindih yang mengganggu karena kedua implementasi menyetujui implementasi --- perilaku tidak terdefinisi/kode mati.

Komentar tentang ini dari masalah lain: https://github.com/rust-lang/rust/pull/41904#issuecomment -300908910

Siapa pun dari tim libs memiliki pemikiran tentang ide scottmcm ? Kedengarannya seperti pendekatan yang bagus bagi saya, dan saya ingin terus mendorong fitur ini ke depan setelah melewatkan siklus rilis lainnya.

Tim libs membicarakan hal ini lagi beberapa minggu yang lalu (maaf atas keterlambatan dalam menulis ini)! Kami sampai pada kesimpulan bahwa sumber masalah atas fitur ini terutama adalah bahwa kasus FFI tidak benar-benar cocok dengan kasus penggunaan lain dari sifat-sifat ini - ini unik karena Anda menyebutnya pada tipe konkret melalui alias yang bervariasi berdasarkan sasaran.

Jadi, rencana tindakan dasar adalah menambahkan impl<T, U> TryFrom<T> for U where U: From<T> dan menghapus implikasi eksplisit untuk konversi bilangan bulat yang tidak dapat salah. Untuk menangani kasus penggunaan FFI, kami akan membuat API terpisah.

Menggunakan alias tipe untuk menghindari pemblokiran pada ! adalah hal yang menarik. Satu-satunya kekhawatiran saya adalah jika ! lebih "khusus" daripada tipe tak berpenghuni normal yang akan menyebabkan kerusakan ketika kami menukar alias dari enum tak berpenghuni ke ! .

Saya telah membuka PR untuk "API terpisah" untuk tipe integral: https://github.com/rust-lang/rust/pull/42456

Saya tidak pernah harus menulis kode generik yang diparameterisasi oleh TryInto atau TryFrom meskipun memiliki banyak konversi, jadi bentuk terakhir sudah cukup untuk semua penggunaan saya dalam tipe yang saya definisikan. Saya pikir memiliki parameter TryInto<...> atau TryFrom<...> sepertinya merupakan bentuk yang dipertanyakan.

Saya bermaksud menggunakan TryFrom setelah stabil sebagai bagian dari sifat turunan, dan akan sangat aneh untuk memanggil metode intrinsik ad-hoc pada beberapa jenis sebagai bagian dari makro derive .

Tolong jangan hapus ini.

Saya tidak pernah harus menulis kode generik yang diparameterisasi oleh TryInto atau TryFrom

Bahkan jika itu masalahnya, saya tidak berpikir itu membuat TryInto dan TryFrom menjadi kurang berguna. Saya menggunakan Into dan From di semua tempat dalam konteks non-generik. Menambahkan impl s dari sifat pustaka standar terasa jauh lebih "normal" dan "diharapkan" daripada sekelompok metode konversi bawaan ad-hoc.

Saya tidak pernah harus menulis kode generik yang diparameterisasi oleh TryInto atau TryFrom

Dari salah satu proyek saya:

pub fn put_str_lossy<C, S> (&self, s: S)
    where C: TryInto<ascii::Char>,
          S: IntoIterator<Item = C>
{
    for c in s.into_iter() {
        self.put_char(match c.try_into() {
            Ok(c) => c,
            Err(_) => ascii::QUESTION_MARK,
        });
    }
}

Apakah diharapkan penerapan sifat-sifat ini akan mengikuti hukum tertentu? Misalnya, jika kita dapat mengonversi A ke B dan B ke A, apakah konversi tersebut harus dapat dibalik jika berhasil?:

#![feature(try_from)]

use std::convert::{TryFrom, TryInto};

fn invertible<'a, A, B, E>(a: &'a A) -> Result<(), E>
    where A: 'a + TryFrom<&'a B>,
          A: PartialEq,
          B: 'a + TryFrom<&'a A>,
          E: From<<A as TryFrom<&'a B>>::Error>,
          E: From<<B as TryFrom<&'a A>>::Error>,
{
    let b = B::try_from(a)?;
    let a2 = A::try_from(&b)?;
    assert!(a == &a2);
    Ok(())
}

edit: s/refleksif/dapat dibalik/

@briansmith Mengingat cara kerja From , menurut saya itu tidak dapat dibalikkan dengan cara ini.

use std::collections::BinaryHeap;

fn main() {
    let a = vec![1, 2];
    let b = BinaryHeap::from(a.clone());
    let c = Vec::from(b);
    assert_ne!(a, c);
}

Jadi saya bertanya-tanya tentang implementasi sifat ini saat ini. Seperti yang muncul di #43127 (lihat juga #43064), saya tidak tahu apakah itu murni karena implementasinya menggunakan i/u128, tetapi sepertinya panggilan TryInto tidak sebaris. Ini tidak terlalu optimal, dan tidak benar-benar dalam semangat abstraksi tanpa biaya. Misalnya menggunakan <u32>::try_into<u64>() harus dioptimalkan hingga tanda sederhana yang diperluas di perakitan akhir (asalkan platformnya 64-bit), tetapi sebaliknya menghasilkan panggilan fungsi.

Apakah ada persyaratan bahwa implementasi sifat harus sama untuk semua tipe bilangan bulat?

Menurut #42456 kita mungkin tidak akan memiliki impl TryFrom langsung pada tipe integer, tetapi bagaimana sifat "NumCast" (yang #43127 harus beralih ke) terlihat masih sedang disusun.

Terlepas dari apakah mereka akhirnya pindah ke sifat lain, konversi ini diterapkan hari ini di libcore dan dapat digunakan di libcore. Saya pikir menggunakan u128 / i128 untuk semua konversi bilangan bulat dilakukan untuk kesederhanaan kode sumber. Kita mungkin harus memiliki kode yang berbeda tergantung pada apakah jenis sumber lebih lebar atau lebih sempit dari tujuan. (Mungkin dengan panggilan makro yang berbeda berdasarkan #[cfg(target_pointer_width = "64")] vs #[cfg(target_pointer_width = "32")] vs #[cfg(target_pointer_width = "16")] .)

Pengingat: tidak banyak yang diperlukan untuk membuka blokir yang satu ini! Jika Anda siap, lihat ringkasan @sfackler dan jangan ragu untuk ping saya atau anggota tim libs lainnya untuk mendapatkan panduan.

Saya tidak menyadari ada dukungan dari tim libs bahwa kami dapat menggunakan ide @scottmcm sebagai solusi untuk tipe bang yang tidak stabil. Saya dapat mengerjakan PR untuk membuat perubahan yang disebutkan @sfackler menggunakan solusi.

Luar biasa, terima kasih @jimmycuadra!

Sebagian besar diskusi ini tampaknya berkisar pada penerapan TryFrom untuk tipe integer. Apakah TryFrom menjadi stabil atau tidak seharusnya tidak hanya karena tipe ini, menurut saya.

Ada konversi rapi lainnya yang akan mendapat manfaat dari sifat-sifat ini seperti TryFrom<&[T]> untuk &[T; N] . Saya baru-baru ini mengirimkan PR untuk menerapkan persis ini: https://github.com/rust-lang/rust/pull/44764 .

Konversi seperti ini cukup penting bagi saya untuk menstabilkan TryFrom .

Dengan #44174 digabungkan, saya yakin ini sekarang tidak diblokir.

PR tersebut menghapus implementasi otomatis FromStr untuk semua tipe yang mengimplementasikan TryFrom<&str> karena sistem tipe saat ini tidak dapat mendukungnya, bahkan dengan spesialisasi. Tujuannya adalah agar FromStr dan parse tidak digunakan lagi demi TryFrom<&str> dan try_into setelah fitur ini distabilkan. Sangat disayangkan kehilangan kompatibilitas sementara di antara keduanya—jika ada yang punya ide untuk sementara, silakan angkat bicara.

Jika tidak ada lagi perubahan yang harus dilakukan dan seseorang di tim libs menyalakan lampu hijau ini untuk stabilisasi, saya dapat melakukan PR stabilisasi dan PR untuk menghentikan FromStr / parse .

dan PR untuk menghentikan FromStr/parse.

Peringatan penghentian tidak boleh ditambahkan ke Nightly hingga penggantian tersedia di Stable (atau hingga https://github.com/rust-lang/rust/issues/30785 diimplementasikan), sehingga memungkinkan setiap saat untuk membuat pembuatan peti tanpa peringatan di ketiga saluran rilis.

Saya melewatkan PR lainnya karena referensi tidak menghasilkan notifikasi email. Saya perhatikan ada spesifik impl From<Infallible> for TryFromIntError . Bukankah seharusnya impl<T> From<Infallible> for T seperti yang dibahas?

@jethrogb Sayangnya, itu bertentangan dengan impl<T> From<T> for T jadi tidak bisa dilakukan (sampai kita mendapatkan persimpangan impls? -- dan menggunakan ! juga tidak berfungsi di sana).

@scottmcm ah tentu saja.

Saya tidak berpikir Anda perlu impls persimpangan? Bukankah itu hanya spesialisasi langsung?

Saya belum membaca komentar lain, tetapi TryFrom rusak untuk saya sekarang (sebelumnya berfungsi dengan baik).

versi rustc:

rustc 1.22.0-nightly (d6d711dd8 2017-10-10)
binary: rustc
commit-hash: d6d711dd8f7ad5885294b8e1f0009a23dc1f8b1f
commit-date: 2017-10-10
host: x86_64-unknown-linux-gnu
release: 1.22.0-nightly
LLVM version: 4.0

Bagian kode yang relevan yang dikeluhkan karat ada di sini: https://github.com/fschutt/printpdf/blob/master/src/types/plugins/graphics/two_dimensional/image.rs#L29 -L39 dan https://github .com/fschutt/printpdf/blob/master/src/types/plugins/graphics/xobject.rs#L170 -L200 - itu dikompilasi dengan baik beberapa minggu yang lalu, itulah sebabnya perpustakaan masih memiliki lencana "build passing".

Namun, pada build nightly terbaru, TryFrom tampaknya rusak:

error[E0119]: conflicting implementations of trait `std::convert::TryFrom<_>` for type `types::plugins::graphics::two_dimensional::image::Image`:
  --> src/types/plugins/graphics/two_dimensional/image.rs:29:1
   |
29 | / impl<T: ImageDecoder> TryFrom<T> for Image {
30 | |     type Error = image::ImageError;
31 | |     fn try_from(image: T)
32 | |     -> std::result::Result<Self, Self::Error>
...  |
38 | |     }
39 | | }
   | |_^
   |
   = note: conflicting implementation in crate `core`

error[E0119]: conflicting implementations of trait `std::convert::TryFrom<_>` for type `types::plugins::graphics::xobject::ImageXObject`:
   --> src/types/plugins/graphics/xobject.rs:170:1
    |
170 | / impl<T: image::ImageDecoder> TryFrom<T> for ImageXObject {
171 | |     type Error = image::ImageError;
172 | |     fn try_from(mut image: T)
173 | |     -> std::result::Result<Self, Self::Error>
...   |
199 | |     }
200 | | }
    | |_^
    |
    = note: conflicting implementation in crate `core`

error: aborting due to 2 previous errors

error: Could not compile `printpdf`.

Jadi, seharusnya ia memiliki implementasi duplikat di crate core . Jika ada yang bisa melihat ini, itu bagus, terima kasih.

@fschutt Impl yang bertentangan mungkin impl<T, U> TryFrom<T> for U where U: From<T> , ditambahkan di https://github.com/rust-lang/rust/pull/44174. Mungkin ada T seperti T: ImageDecoder dan Image: From<T> .

Apakah ada sesuatu yang masih diperlukan untuk meninggalkan gerbang fitur?

Jika https://github.com/rust-lang/rust/issues/35121 distabilkan terlebih dahulu, kami dapat menghapus tipe Infallible yang diperkenalkan di https://github.com/rust-lang/rust/pull/ 44174 dan gunakan ! sebagai gantinya. Saya tidak tahu apakah ini dianggap sebagai persyaratan.

Saya pikir pemblokir utama di sini masih tipe integer. Entah kita menggunakan sifat Cast terpisah untuk tipe integer https://github.com/rust-lang/rust/pull/42456#issuecomment -326159595, atau membuat serat portabilitas #41619 terjadi terlebih dahulu.

Jadi saya dulu memiliki enum yang mengimplementasikan TryFrom untuk AsRef<str> , tetapi ini rusak beberapa bulan yang lalu. Saya pikir itu adalah bug yang diperkenalkan di malam hari yang akan hilang seiring waktu, tetapi tampaknya tidak demikian. Apakah ini bukan lagi pola yang didukung untuk TryFrom ?

impl<S: AsRef<str>> TryFrom<S> for MyEnum {
    type Error = &'static str;

    fn try_from(string: S) -> Result<Self, Self::Error> {
        // Impl here
    }
}
...

Opsi apa lagi yang tersedia untuk mengonversi dari &str dan String ?

@kybishop apakah MyEnum mengimplementasikan FromStr ? Itu mungkin sumber kerusakan Anda.

@nvzqz tidak, meskipun saya disarankan untuk menggunakan TryFrom melalui Rust IRC karena ini adalah solusi yang lebih umum. TryFrom awalnya bekerja dengan baik sampai perubahan besar terjadi di malam hari beberapa bulan yang lalu.

EDIT: Apakah maksud Anda saya harus beralih ke implementasi FromStr , atau jika _did_ impl FromStr , itu dapat menyebabkan kerusakan?

EDIT 2: Inilah impl lengkapnya, agak pendek dan sederhana bagi mereka yang penasaran: https://Gist.github.com/kybishop/2fa9e9d32728167bed5b1bc0b9becd97

@kybishop apakah ada alasan tertentu Anda menginginkan implementasi untuk AsRef<str> daripada &str ?

@sfackler Saya mendapat kesan itu memungkinkan konversi dari &str dan String , meskipun saya masih sedikit pemula Rust, jadi mungkin saya salah paham persis bagaimana AsRef<str> digunakan. Saya akan mencoba mematikan &str dan melihat apakah AsRef mengizinkan sesuatu yang &str tidak.

@kybishop Sama seperti https://github.com/rust-lang/rust/issues/33417#issuecomment -335815206, dan seperti yang dikatakan oleh pesan kesalahan kompiler, ini adalah konflik nyata dengan impl impl<T, U> std::convert::TryFrom<U> for T where T: std::convert::From<U> itu ditambahkan ke libcore.

Mungkin ada tipe T (mungkin di peti hilir) yang mengimplementasikan keduanyaFrom<MyEnum> dan AsRef<str>dan memiliki MyEnum: From<T> . Dalam hal ini, kedua impls akan berlaku, jadi kedua impls tidak boleh ada bersama-sama.

@SimonSapin mengerti. Jadi apa pilihan bagi orang yang ingin mengonversi dari &str dan &String ? Hanya perlu mengimplikasikan TryFrom untuk keduanya?

EDIT: Saya kira saya bisa makan pekerjaan ekstra dan menelepon .as_ref() di String s. Saya kemudian dapat memiliki satu TryFrom impl untuk str .

Ya, dua impls (mungkin dengan satu berdasarkan yang lain, seperti yang Anda tunjukkan) harus berfungsi selama MyEnum tidak mengimplementasikan From<&str> atau From<String> .

@kybishop String mengimplementasikan Deref<str> , jadi ketik inferensi harus memungkinkan &String untuk memaksa ke &str ketika meneruskannya ke TryFrom impl. Memanggil as_ref mungkin tidak selalu diperlukan.

Dengan https://github.com/rust-lang/rust/pull/47630 akan menstabilkan ! , apakah ada selera untuk PR untuk mengganti Infallible dengan ! di sini ?

Rute yang lebih baik untuk diikuti adalah membuat alias. Ini melestarikan ekspresi dan menggunakan fitur bahasa yang disesuaikan.

type Infallible = !;

Langsung saja. Saya setuju dengan @scottmcm tentang itu.

Namun, ini menambahkan overhead ekstra bahwa fitur ini ( TryInto / TryFrom ) sekarang bergantung pada fitur tidak stabil lainnya – never_type .

Juga, Infallible memiliki keuntungan yang memberikan lebih banyak informasi / semantik tentang mengapa tipe tidak dapat dibangun. Saya berpendapat dengan diri saya sendiri sekarang.

Saya ingin ! menjadi tipe kosakata yang cukup sehingga Result<_, !> akan secara intuitif dibaca sebagai "hasil yang tidak dapat salah", atau "hasil yang (secara harfiah) tidak pernah Err." Menggunakan alias (apalagi jenis yang terpisah!) menurut saya agak berlebihan dan saya dapat melihatnya menyebabkan setidaknya untuk diri saya sendiri jeda sesaat ketika membaca tanda tangan jenis—"tunggu, bagaimana ini berbeda dari ! lagi ?" YMMV, tentu saja.

@jdahlstrom Saya sangat setuju. Itu perlu kita perkenalkan di buku Rust atau nomicon agar “ground truth” dan bersahabat.

Sekarang sudah dua tahun sejak RFC untuk API ini diajukan.

~ @briansmith : Ada PR stabilisasi yang sedang berlangsung.~

EDIT : Atau mungkin saya terlalu lelah ...

Anda menautkan PR stabilisasi tipe ! .

Karena PR stabilisasi ! baru saja digabungkan, saya telah mengirimkan PR untuk mengganti convert::Infallible dengan ! : #49038

https://github.com/rust-lang/rust/pull/49038 digabungkan. Saya yakin ini adalah pemblokir terakhir untuk stabilisasi, beri tahu saya jika saya melewatkan masalah yang belum terselesaikan.

@rfcbot menggabungkan fcp

rfcbot tidak merespons karena FCP lain telah diselesaikan sebelumnya di https://github.com/rust-lang/rust/issues/33417#issuecomment -302817297.

Uhmm, ada beberapa impls yang harus ditinjau kembali. Sekarang kita memiliki impl<T, U> TryFrom<U> for T where T: From<U> , sisa impls dari TryFrom yang memiliki type Error = ! harus diganti dengan impls From , dihapus, atau dibuat salah ( mengubah jenis kesalahan menjadi beberapa jenis yang tidak berpenghuni).

Dalam kasus itu saya dapat menemukan melibatkan usize atau isize . Saya kira impls From yang sesuai tidak ada karena kesalahannya tergantung pada ukuran pointer target. Memang impls TryFrom dihasilkan secara berbeda untuk target yang berbeda: https://github.com/rust-lang/rust/blob/1.24.1/src/libcore/num/mod.rs#L3103 -L3179 ​​Ini mungkin merupakan bahaya portabilitas.

dihasilkan secara berbeda untuk target yang berbeda

Untuk memperjelas: badan metode yang berbeda untuk target_pointer_width yang berbeda dalam impl yang sama baik-baik saja (dan mungkin perlu), API yang berbeda (jenis kesalahan) tidak.

Stabilisasi PR: #49305. Setelah beberapa diskusi di sana, PR ini juga menghapus beberapa impls TryFrom yang melibatkan usize atau isize karena kami belum memutuskan antara dua cara berbeda untuk mengimplementasikannya. (Dan tentu saja kita hanya dapat memiliki satu.)

Masalah pelacakan khusus untuk mereka: https://github.com/rust-lang/rust/issues/49415

TryFrom bekerja dengan sempurna pada rustc 1.27.0-nightly (ac3c2288f 2018-04-18) tanpa fitur gating tetapi rusak ketika dikompilasi dengan rustc 1.27.0-nightly (66363b288 2018-04-28) .

Apakah ada kemunduran pada stabilisasi fitur ini dalam 10 hari terakhir?

@kjetilkjeka Fitur ini baru-baru ini tidak stabil: #50121.

(Dibuka kembali sejak stabilisasi telah dikembalikan)

@kjetilkjeka Stabilisasi TryFrom telah dikembalikan di https://github.com/rust-lang/rust/pull/50121 bersama dengan stabilisasi tipe ! , karena TryFrom<U, Error=!> for T where T: From<U> impl. Jenis ! tidak stabil karena https://github.com/rust-lang/rust/issues/49593.

Terima kasih telah menjelaskan. Apakah ini berarti bahwa fitur ini pada dasarnya diblokir pada beberapa perubahan pada paksaan tipe kompiler? Saya tidak dapat menemukan masalah yang menjelaskan perubahan apa yang diperlukan, apakah besarnya perubahan diketahui pada saat ini?

Apakah ada alasan mendasar mengapa kami tidak dapat melanjutkan dan menstabilkan sifat TryFrom itu sendiri, dan setiap impls yang tidak melibatkan ! , dan hanya menunda impls stabilisasi yang melibatkan ! sampai setelah kemungkinan stabilisasi ! ?

https://github.com/rust-lang/rust/pull/49305#issuecomment -376293243 mengklasifikasikan berbagai kemungkinan implementasi sifat.

@joshtriplett Sejauh yang saya mengerti, impl<T, U> TryFrom<T> for U where U: From<T> { type Err = !; } khususnya tidak kompatibel ke belakang untuk ditambahkan jika TryFrom sudah stabil.

@SimonSapin
Mengapa itu tidak kompatibel ke belakang untuk ditambahkan?

(Dan apakah kita benar-benar membutuhkan selimut?)

Mengapa itu tidak kompatibel ke belakang untuk ditambahkan?

Saya pikir ini tidak kompatibel ke belakang karena TryFrom dapat diimplementasikan secara manual antara TryFrom distabilkan dan ! distabilkan. Ketika implementasi selimut ditambahkan, itu akan bertentangan dengan implementasi manual.

(Dan apakah kita benar-benar membutuhkan selimut?)

Saya pikir blanket impl benar-benar masuk akal ketika menulis kode generik di atas TryFrom . Ketika mengacu pada semua jenis yang memiliki kemungkinan untuk dikonversi menjadi T Anda akan sering juga ingin memasukkan semua jenis yang dapat dikonversi menjadi T pasti. Saya berasumsi alternatifnya adalah meminta semua orang untuk juga mengimplementasikan TryFrom untuk semua jenis yang mengimplementasikan From dan menunggu spesialisasi sebelum membuat blanket impl. Anda masih akan memiliki masalah dengan Err untuk membuat Result menjadi generik. ! sepertinya cara yang bagus untuk mengekspresikan dan secara terprogram menegakkan infalibilitas konversi, dan mudah-mudahan layak untuk ditunggu.

Kami selalu dapat menunggu hingga spesialisasi tersedia untuk menyediakan blanket impl, dan menstabilkan berbagai konversi numerik untuk sementara.

Saya telah melihat berbagai permintaan untuk ini dalam konteks orang yang mencari bantuan tentang bagaimana melakukan konversi bilangan bulat secara idiomatis di Rust, dan baru hari ini bertemu dengan seseorang yang secara khusus bertanya-tanya bagaimana mereka harus mengonversi u64 ke u32 dengan deteksi kesalahan.

Saya tidak yakin bahwa spesialisasi akan secara ajaib memungkinkan penambahan blanket impl setelah fakta. Pemahaman saya tentang proposal saat ini adalah bahwa suatu sifat harus dipilih untuk dapat dispesialisasikan, yang mungkin tidak kompatibel ke belakang untuk dilakukan pada sifat yang ada.

Untuk setiap kali ini distabilkan: _please_ tambahkan TryFrom dan TryInto ke pendahuluan.

@SergioBenitez Kami telah melakukannya di Nightly untuk sementara waktu, dan sayangnya itu adalah perubahan besar untuk peti yang menentukan sifat TryFrom atau TryInto mereka sendiri (untuk digunakan sementara std yang tidak stabil), cukup bahwa kami telah mengembalikannya.

Saya pikir pelajaran untuk dipelajari di sini untuk tim std libs adalah bahwa ketika ada sifat yang mungkin ingin kita tambahkan ke pendahuluan, kita harus melakukannya segera setelah itu diterapkan dan tidak menunggu stabilisasi.

Namun, untuk TryFrom dan TryInto , solusinya adalah memiliki pendahuluan yang berbeda untuk edisi 2018. Ini telah dibahas secara informal sebelumnya tetapi saya tidak menemukannya, jadi saya membuka https://github.com/rust-lang/rust/issues/51418 .

Solusi umum untuk masalah serupa diterapkan untuk ekstensi
metode di https://github.com/rust-lang/rust/issues/48919 , di mana tidak stabil
metode ekstensi mengeluarkan peringatan ketika kode stabil bertabrakan.

Sepertinya Anda bisa melakukan sesuatu yang mirip dengan istilah baru yang ditambahkan ke
pendahuluan?

Pada Kam, 7 Juni 2018, 11:47 Simon [email protected] menulis:

@SergioBenitez https://github.com/SergioBenitez Kami telah melakukannya pada
Setiap malam untuk sementara waktu, dan sayangnya itu adalah perubahan besar untuk peti
yang mendefinisikan sifat TryFrom atau TryInto mereka sendiri (untuk digunakan saat std
yang tidak stabil), cukup kami mengembalikannya.

Saya pikir pelajaran yang harus dipelajari di sini untuk tim std libs adalah kapan
ada sifat yang mungkin ingin kita tambahkan ke pendahuluan, kita harus melakukannya
jadi segera setelah itu dilaksanakan dan tidak menunggu stabilisasi.

Namun untuk TryFrom dan TryInto, solusinya adalah memiliki
pendahuluan untuk edisi 2018. Ini telah dibahas secara informal sebelumnya tapi
Saya tidak menemukannya diajukan, jadi saya membuka #51418
https://github.com/rust-lang/rust/issues/51418 .


Anda menerima ini karena Anda berlangganan utas ini.
Balas email ini secara langsung, lihat di GitHub
https://github.com/rust-lang/rust/issues/33417#issuecomment-395525170 ,
atau matikan utasnya
https://github.com/notifications/unsubscribe-auth/AAC2lNbHvgBjWBk48-1UO311-LuUY5lPks5t6XUvgaJpZM4IXpys
.

Dalam hal ini yang bertabrakan bukanlah sifat itu sendiri (nama item di pendahuluan) tetapi metode yang dimasukkan ke dalam cakupan oleh sifat itu. Mungkin masih ada tweak serupa yang bisa kita lakukan, tapi lebih halus.

@SimonSapin apa rencana saat ini untuk stabilisasi TryFrom/TryInto? Saya tidak dapat menemukan apa pun yang konkret di luarnya yang dikembalikan pada 29 April.

@nayato Ini diblokir untuk menstabilkan (sekali lagi) yang tidak pernah ketik https://github.com/rust-lang/rust/issues/35121 , yang dengan sendirinya diblokir di https://github.com/rust-lang/rust/ masalah/49593.

@SimonSapin Jenis never hanya digunakan di sini . Bisakah kita menstabilkan TryFrom tanpa implementasi itu dan mendaratkannya saat tidak pernah distabilkan? TryFrom adalah antarmuka yang cukup penting bahkan terlepas dari kesetaraan dengan Try .

Sayangnya tidak. Ini akan menjadi perubahan besar untuk menambahkan selimut impl ini setelah sifat telah distabilkan dan perpustakaan crates.io memiliki kesempatan untuk mengimplementasikan misalnya TryFrom<Foo> for Bar sementara mereka juga memiliki From<Foo> for Bar , sebagai impls akan tumpang tindih.

Keputusan sejauh ini adalah membuat TryFrom "kompatibel" dengan cara ini dengan From cukup penting untuk memblokir stabilisasi.

Mungkin pemikiran yang naif dan konyol:

Bagaimana jika rustc mendapatkan lint kesalahan yang selalu aktif sementara itu yang memicu Error<_, !> , mencegah impls apa pun di userland, memungkinkan penambahan impl nanti?

Atau impl<T: From<U>, U> TryFrom<U> for T yang tidak stabil dengan jenis kesalahan apa pun. Bisakah sifat impls menjadi tidak stabil?

@jethrogb Tidak

Saya tidak yakin saya sepenuhnya mengerti mengapa ini diblokir pada tipe yang tidak pernah, tetapi fakta bahwa ini menunjukkan kepada saya bahwa mekanisme penting hilang dari Rust. Tampaknya harus ada cara untuk mencadangkan implementasi yang diinginkan untuk definisi di masa mendatang. Mungkin mekanisme itu seperti membuatnya tidak stabil, tetapi idealnya itu adalah mekanisme yang dapat digunakan oleh peti non-std juga. Adakah yang mengetahui solusi yang diusulkan untuk masalah ini?

Itu diblokir karena implikasi ini:

impl<T, U> TryFrom<U> for T where T: From<U> {
    type Error = !;

    fn try_from(value: U) -> Result<Self, Self::Error> {
        Ok(T::from(value))
    }
}

@rust-lang/libs Apa pendapat Anda tentang kembali ke enum Infallible {} alih-alih tidak pernah mengetik, untuk membuka blokir?

Saya pribadi tidak keberatan memiliki enum Infalliable {} dan kemudian mengubahnya menjadi type Infalliable = ! .

Kami membahas ini dalam pertemuan triase libs kemarin, tetapi tidak dapat mengingat apakah perubahan seperti itu akan baik-baik saja setelah stabilisasi atau jika kami telah menolak gagasan itu karena potensi kerusakan, atau kerusakan apa yang akan terjadi.

Apa yang kami ingat adalah bahwa kami hanya dapat melakukan satu penggantian seperti itu: jika dua enum perpustakaan standar yang lebih stabil (tipe terpisah) kemudian menjadi tipe yang sama, peti yang memiliki beberapa impl untuk keduanya akan rusak karena impls menjadi tumpang tindih (atau identik). Misalnya impl From<std::convert::Invallible> for MyError dan impl From<std::string::ParseError> for MyError .

Omong-omong, kami memiliki std::string::ParseError yang sejauh yang saya tahu adalah satu-satunya enum kosong di perpustakaan standar 1.30.0. Jadi jika kami yakin tidak ada masalah lain dengan rencana ini, kami dapat:

  • Pindahkan string::ParseError ke convert::Infallible
  • Ekspor ulang di lokasi lamanya dengan pub use atau pub type (apakah ini membuat perbedaan?)
  • Gunakan itu dalam impl selimut TryFrom
  • Kemudian, dalam rilis yang sama seperti ketika tipe never distabilkan, ganti string::ParseError dan convert::Infallible dengan type _ = ! dan gunakan ! langsung di tempat mereka digunakan.
  • (Opsional) Nanti, pancarkan peringatan penghentian untuk alias lama

Baru saja dan dengan enggan menambahkan sifat placeholder TryFrom ke peti saya sendiri, dan memang tanpa sepenuhnya memahami implikasi dari RFC dan upaya stabilisasi, saya terkejut bahwa selimut TryFrom untuk From dengan Infallible / ! jenis kesalahan apa yang menahan ini? Bukankah ini tujuan sekunder, setelah menetapkan sifat stabil std TryFrom dan menyelimuti TryInto ? Maksud saya, meskipun tidak ada kenaikan From menjadi TryFrom (maksudnya saya tidak sepenuhnya mengerti), bukankah lebih sedikit churn jika setiap peti yang membutuhkannya tidak ditambahkan sendiri TryFrom ?

Masalah yang jelas jika TryFrom dikirimkan tanpa blanket impl untuk From adalah bahwa peti mungkin mengimplementasikan TryFrom dan From untuk jenis yang sama (mungkin persis karena blanket impl tidak ada), dan mereka akan pecah ketika blanket impl ditambahkan ke libstd.

Meskipun, kalau dipikir-pikir, mungkin itu bukan perubahan besar dengan spesialisasi?

Hmm, tolong maafkan saya lagi jika saya melewatkan yang jelas . Hampir seperti pengembaraan RFC/pelacakan 1,5 tahun ini membutuhkan bagian FAQ yang berjalan untuk memahami perkembangan logika. Bagaimana jika panduannya hanya menerapkan From hanya untuk konversi yang sempurna, dan TryFrom hanya untuk konversi yang dapat salah? Bukankah itu memberikan hasil akhir praktis yang serupa, sambil memberikan lebih sedikit penghalang untuk mengirimkannya dalam peti std dan penyesuaian?

Ya, seperti yang dikatakan @glandium , menambahkan impl selimut setelah sifat-sifatnya stabil adalah perubahan yang luar biasa. Stabilisasi belum siap, dan tidak jelas apakah itu akan memungkinkan impl persimpangan semacam ini (daripada hanya impl "lebih spesifik").

Memberikan panduan (dokumen?) yang mengatakan jangan menulis program yang akan rusak tidak cukup baik untuk membenarkan perubahan yang rusak. Kerusakan pasti akan tetap terjadi.

Apa yang diperlukan agar rencana yang diuraikan di https://github.com/rust-lang/rust/issues/33417#issuecomment -423073898 mulai terjadi? Apa yang bisa dilakukan untuk membantu?

Ini sedikit tugas tanpa pamrih, tetapi akan membantu jika seseorang dapat melalui masalah pelacakan ini dan https://github.com/rust-lang/rust/issues/35121 untuk memeriksa apakah ada masalah dengan rencana yang kami ' telah dibahas sebelumnya dan dilupakan sejak, khususnya apakah mengganti enum Infallible dengan type Infallible = !; setelah enum stabil di rilis sebelumnya bisa menjadi perubahan yang melanggar.

Apakah Infallible enum perlu distabilkan bersama dengan sifat? Jika tetap tidak stabil, tidak ada yang bisa menamainya, jadi menukarnya dengan ! nanti akan baik-baik saja?

@seanmonstar Tidak, Anda dapat merujuknya menggunakan <u16 as TryFrom<u8>>::Error dan itu dianggap sebagai nama yang stabil. Saksi:

// src/lib.rs
#![feature(staged_api)]
#![stable(since = "1.0.0", feature = "a")]

#[stable(since = "1.0.0", feature = "a")]
pub trait T1 {
    #[stable(since = "1.0.0", feature = "a")]
    type A;
}

#[unstable(issue = "12345", feature = "b")]
pub struct E;

#[stable(since = "1.0.0", feature = "a")]
impl T1 for u8 {
    type A = E;
}
// src/bin/b.rs
extern crate a;

trait T3 {}

impl T3 for <u8 as a::T1>::A {}
impl T3 for a::E {}

fn main() {}

Impl T3 pertama tidak menyebabkan kesalahan apa pun. Hanya impl T3 kedua yang menyebabkan kesalahan "penggunaan fitur perpustakaan tidak stabil" E0658.

Itu.... Wah, cuma minta digigit >_<

Saya pribadi menggunakan trik membuat tipe menjadi publik dalam modul yang tidak diekspor untuk mengembalikan tipe yang tidak dapat disebutkan namanya, dan sementara seseorang melakukan apa yang Anda katakan akan berarti kerusakan jika mereka melakukan itu, perasaan saya "malu pada mereka", dan jangan pertimbangkan menyesuaikan jenis yang tidak dapat disebutkan namanya sebagai melanggar.

Saya pribadi tidak keberatan memastikan bahwa setiap enum yang tidak benar-benar tidak pernah di libstd ini adalah sama, kemudian mengubahnya menjadi alias tipe menjadi ! ketika itu menjadi stabil. Tampaknya masuk akal bagi saya.

Tidak terkait dengan semua keributan yang tidak pernah terjadi ini, apa pilihan desain untuk konversi yang salah pada tipe non- Copy ? Perhatian harus diberikan untuk menghindari menjatuhkan input yang diberikan, sehingga dapat diambil kembali jika terjadi kegagalan.

Misalnya, dengan String::from_utf8 , kita dapat melihat bahwa jenis kesalahan berisi input yang dimiliki Vec<u8> sehingga dapat mengembalikannya :

// some invalid bytes, in a vector
let sparkle_heart = vec![0, 159, 146, 150];

match String::from_utf8(sparkle_heart) {
    Ok(string) => {
        // owned String binding in this scope
        let _: String = string;
    },
    Err(err) => {
        let vec: Vec<u8> = err.into_bytes(); // we got the owned vec back !
        assert_eq!(vec, vec![0, 159, 146, 150]);
    },
};

Jadi, jika kita ingin mendapatkan implementasi String: TryFrom<Vec<u8>> , diharapkan <String as TryFrom<Vec<u8>>>::Error menjadi FromUtf8Error kan?

Ya, memberikan nilai input "kembali" pada jenis kesalahan adalah opsi yang valid. Mengambil referensi dengan impl<'a> TryFrom<&'a Foo> for Bar adalah hal lain.

Namun dalam contoh khusus ini saya tidak yakin bahwa impl TryFrom sesuai. UTF-8 hanyalah salah satu kemungkinan penguraian kode byte ke dalam Unicode, dan from_utf8 mencerminkan hal itu dalam namanya.

Ini sedikit tugas tanpa pamrih, tetapi akan membantu jika seseorang dapat melalui masalah pelacakan ini dan #35121 untuk memeriksa apakah ada masalah dengan rencana yang telah kita diskusikan sebelumnya dan terlupakan, khususnya apakah mengganti enum Infallible dengan type Infallible = !; setelah enum stabil di rilis sebelumnya bisa menjadi perubahan yang melanggar.

Tidak ada masalah nyata yang ditunjukkan dalam masalah ini atau #35121. Ada satu kekhawatiran seputar kemungkinan ! menjadi spesial dalam beberapa hal, tipe tanpa hambatan tidak. Tetapi tidak ada kekhawatiran dalam PR dan jelas dari komentar tinjauan kode bahwa enum menjadi stabil adalah suatu kemungkinan (meskipun itu tidak pernah terjadi). Berikut ini tautan ke apa yang saya temukan.

Konsep asli
Satu perhatian abstrak
Silakan dari tim lib

Diikuti oleh:

44174 Sep 29: menambahkan tipe Infallible

47630 14 Mar: stabilkan tipe never !

49038 22 Mar: dikonversi Infallible ke tipe never !

49305 27 Mar: menstabilkan TryFrom

49518 30 Mar: dihapus dari pendahuluan

50121 21 Apr: tidak stabil

Pada catatan implikasi selimut ketika ! menjadi stabil,

apakah ini akan berhasil?

impl<T, U> TryFrom<U> for T
where U: Into<T> {
    type Err = !;

    fn try_from(u: U) -> Result<Self, !> { Ok(u.into()) }
}

impl<T, U> TryInto<U> for T
where U: TryFrom<T> {
    type Err = U::Err;

    fn try_into(self) -> Result<U, !> { U::try_from(self) }
}

Dengan cara ini semua konversi sempurna (dengan From dan Into ) mendapatkan TryFrom dan TryInto yang sesuai dengan Err adalah ! dan kami mendapatkan impl TryInto untuk setiap TryFrom impl seperti bagaimana kami mendapatkan impl Into untuk setiap From impl.

Dengan cara ini jika Anda ingin menulis impl, Anda akan mencoba From lalu Into lalu TryFrom lalu TryInto , dan salah satunya akan bekerja untuk skenario Anda , jika Anda membutuhkan konversi yang sempurna, Anda akan menggunakan From atau Into (berdasarkan aturan koherensi) dan jika Anda membutuhkan konversi yang dapat salah, Anda akan menggunakan TryFrom atau TryInto (berdasarkan aturan koherensi). Jika Anda membutuhkan batasan, Anda dapat memilih TryInto atau Into berdasarkan apakah Anda dapat menangani konversi yang salah. TryInto akan menjadi semua konversi, dan Into akan menjadi semua konversi yang sempurna.

Kemudian kita dapat menggunakan impl TryFrom<T, Err = !> atau impl TryInto<T, Err = !> dengan petunjuk untuk menggunakan impl From<T> atau impl Into<T> .

Ah, tidak melihatnya (terlalu banyak impls lain mengacaukan dokumen). Bisakah kita berubah?

impl<T, U> TryFrom<U> for T where    T: From<U>,

ke

impl<T, U> TryFrom<U> for T where    U: Into<T>,

karena ini akan memungkinkan lebih banyak impl, dan tidak akan menghukum orang yang hanya bisa impl Into untuk alasan koherensi, dengan cara itu mereka juga mendapatkan impl otomatis untuk TryFrom , dan From impl akan diterapkan secara transitif karena blanket impl untuk Into .

Apakah Anda ingin mencobanya, melihat apakah itu dikompilasi, dan mengirim permintaan tarik?

Ya saya ingin mencoba.

Apakah ada kasus di mana From<U> for T tidak dapat diimplementasikan, tetapi Into<T> for U dan TryFrom<U> for T dapat?

Ya, misalnya

use other_crate::OtherType;

struct MyType;

// this impl will fail to compile
impl From<MyType> for OtherType {
    fn from(my_type: MyType) -> OtherType {
        // impl details that don't matter
    }
}

// this impl will not fail to compile
impl Into<OtherType> for MyType {
    fn into(self) -> OtherType {
        // impl details that don't matter
    }
}

Ini karena aturan anak yatim.
TryFrom dan From adalah sama dalam hal aturan anak yatim, dan juga TryInto dan Into adalah sama dalam hal aturan anak yatim

@KrishnaSannasi contoh Anda akan dikompilasi, lihat saja contoh taman bermain ini

Saya pikir aturan koherensi terbaru dijelaskan dalam RFC ini dan kedua implementasi harus diizinkan.

struct MyType<T>(T);

impl<T> From<MyType<T>> for (T,) {
    fn from(my_type: MyType<T>) -> Self {
        unimplemented!()
    }
}

impl<T> Into<(T,)> for MyType<T> {
    fn into(self) -> (T,) {
        unimplemented!()
    }
}

tempat bermain
Ah ya, tetapi segera setelah Anda menambahkan parameter generik, itu akan jatuh. Jika Anda mencoba dan mengkompilasi setiap impl satu per satu, From yang satu akan gagal untuk dikompilasi, sedangkan yang Into akan dikompilasi.


Alasan lain saya menginginkan perubahan ini adalah karena saat ini, jika Anda menulis Into impl, Anda tidak melakukannya, dan tidak dapat secara otomatis mendapatkan TryInto impl. Saya melihat ini sebagai desain yang buruk karena tidak konsisten dengan From dan TryFrom .

Saya lebih bertanya apakah pernah ada kasus di mana Anda ingin TryFrom<U> for T diturunkan secara otomatis dari Into<T> for U , tetapi From<U> for T tidak dapat diimplementasikan. Tampaknya tidak masuk akal bagi saya.

Saya tentu mengerti menginginkan Into ke TryInto blanket impl, tetapi sayangnya sistem tipe saat ini tidak mengizinkan blanket impl seperti itu karena bertentangan dengan From lainnya ke TryFrom dan From ke Into blanket impls (atau, setidaknya, itulah pemahaman saya).

Saya cukup yakin bahwa kasus penggunaan ini persis seperti yang seharusnya diperbaiki oleh RFC @kjetilkjeka . Setelah itu diimplementasikan seharusnya tidak pernah ada kasus di mana Anda dapat mengimplementasikan Into tetapi tidak From .

@scottjmaddox Saya tidak ingin Into menyiratkan TryFrom sebanyak yang saya inginkan Into menyiratkan TryInto . Juga From dan Into secara semantik setara, hanya karena kita tidak dapat menyatakan bahwa dalam sistem sifat kita tidak berarti bahwa

TryFrom<U> for T akan diturunkan secara otomatis dari Into<T> for U , tetapi where From<U> for T tidak dapat diimplementasikan.

tidak masuk akal. Di dunia yang sempurna kita tidak akan memiliki perbedaan dan hanya akan ada satu sifat untuk diubah antar jenis, tetapi karena keterbatasan dalam sistem, kita sekarang berakhir dengan dua. Ini tidak akan berubah karena jaminan stabilitas, tetapi kita dapat memperlakukan kedua sifat ini sebagai konsep yang sama dan beralih dari sana.

@clarcharr Bahkan jika itu masalahnya, masih tidak ada cara untuk memiliki keduanya Into menyiratkan TryInto dan TryFrom menyiratkan TryInto secara langsung, sebagai dua selimut impls akan konflik. Saya ingin Into menyiratkan TryInto , dan untuk TryFrom menyiratkan TryInto . Cara saya mengusulkan akan melakukannya, meskipun secara tidak langsung.


Saya membuat komentar pada permintaan tarik saya di mana saya meletakkan alasan utama saya untuk perubahan ini di sini


Ini kurang penting dari alasan di atas, tetapi saya juga menyukai itu dengan impl ini, semua konversi yang tidak dapat salah sekarang akan memiliki pasangan yang dapat salah, di mana Err tipe dari versi yang dapat salah adalah ! .

Untuk memperjelas, saya ingin keempat impls ini

  • From menyiratkan Into (untuk stabilitas)
  • TryFrom menyiratkan TryInto atau TryInto menyiratkan TryFrom (karena secara semantik setara)
  • From menyiratkan TryFrom
  • Into menyiratkan TryInto

Saya tidak peduli apakah itu dilakukan secara langsung atau tidak langsung.

Atau, kita bisa membuat TryInto sebagai alias:

trait TryInto<T> = where T: TryFrom<Self>;

Saya tidak tahu apakah ini benar-benar akan berhasil, tetapi tampaknya cukup sederhana.

Di mana metode into akan didefinisikan?

@clarcharr Seperti yang dikatakan @SimonSapin , di mana metode into akan didefinisikan. Kami tidak memiliki alias sifat. Itu perlu RFC lain. Dan kita perlu memblokir yang ini di RFC itu, yang tidak diinginkan.

@KrishnaSannasi Saat ini tidak mungkin untuk memiliki keempat From => Into , From => TryFrom , TryFrom => TryInto , dan Into => TryInto , karena semua yang mengimplementasikan From akan memiliki dua impl yang bersaing untuk TryInto (satu dari From => Into => TryInto , dan satu lagi dari From => TryFrom => TryInto ).

Karena From => Into dan TryFrom => TryInto keduanya kritis, Into => TryInto harus dikorbankan. Atau setidaknya itu pemahaman saya.

Ya, saya tidak berpikir alias TryInto keluar dengan benar jadi abaikan saja saya ><

Saya setuju dengan apa yang dikatakan @scottjmaddox .

@scottjmaddox

Hanya untuk menjernihkan kemungkinan kesalahpahaman, saya menghapus From auto impls TryFrom dan menggantinya dengan Into auto impls TryFrom .

Saat ini tidak mungkin untuk memiliki keempat From => Into, From => TryFrom, TryFrom => TryInto, dan Into => TryInto

Ini hanya salah, lihat saja implementasi yang diusulkan.

Yang ini:
From -> Into -> TryFrom -> TryInto

From auto-impls Into
Into auto-impls TryFrom
TryFrom auto-impls TryInto

Dengan ekstensi, karena impl otomatis transitif, kami mendapatkan
From menyiratkan TryFrom (karena From implikasi otomatis Into dan Into implikasi otomatis TryFrom )
Into menyiratkan TryInto (karena Into auto impls TryFrom dan TryFrom auto impls TryInto )
masing-masing dengan 1 tingkat tipuan (saya pikir ini baik-baik saja)

Jadi semua syarat saya sudah terpenuhi.
Kita bisa memiliki semuanya

implis | tingkat tipuan
-----------------------|-------------------
From menyiratkan Into | Tidak ada tipuan
TryFrom menyiratkan TryInto | Tidak ada tipuan
From menyiratkan TryFrom | 1 tingkat tipuan
Into menyiratkan TryInto | 1 tingkat tipuan


From -> Into -> TryInto -> TryFrom
Juga akan berfungsi, tetapi saya ingin menjaga konsistensi dan versi asli (lihat di atas) lebih konsisten daripada versi ini


Catatan tentang terminologi: (ketika saya menggunakannya)

-> berarti impl otomatis
impl otomatis mengacu pada impl aktual yang diketik.
menyiratkan berarti jika saya memiliki impl ini saya juga akan mendapatkan impl itu


Perhatikan juga bahwa saya telah membuat permintaan tarik dan semua tes telah berlalu, artinya tidak ada lubang dalam logika saya, ini mungkin. Kami hanya perlu mendiskusikan apakah perilaku ini diinginkan. Saya pikir itu, karena itu akan menjaga konsistensi (yang sangat penting).

tarik permintaan

@KrishnaSannasi Ahhh, sekarang saya mengerti maksud Anda. Ya, itu masuk akal dan sekarang saya melihat bagaimana perubahan yang Anda usulkan memecahkan masalah dan memberikan perilaku yang diinginkan. Terima kasih atas penjelasannya yang menyeluruh!

Sunting : oke, tunggu, meskipun ... Saya masih tidak mengerti mengapa impl selimut saat ini tidak cukup. Mungkin ada kasus di mana Anda dapat mengimplementasikan Into tetapi tidak From ? Namun apakah mungkin untuk melakukan auto-derive TryFrom ?

Sunting 2 : Oke, saya baru saja kembali dan membaca penjelasan Anda tentang bagaimana aturan yatim piatu dapat mencegah implementasi From . Semuanya masuk akal sekarang. Saya sangat mendukung perubahan ini. Masih sedikit frustasi bahwa aturan anak yatim memiliki begitu banyak konsekuensi yang tidak diinginkan, tetapi kami tidak akan memperbaikinya di sini, jadi masuk akal untuk melakukan yang terbaik.

Apakah seseorang dari @rust-lang/libs bersedia memulai FCP sekarang setelah https://github.com/rust-lang/rust/issues/49593 telah diperbaiki dan never_type adalah kandidat untuk stabilisasi sekali lagi?

Fitur ini telah melalui FCP untuk stabilisasi. ( Proposal , selesai .) Saya pikir tidak perlu melalui periode komentar 10 hari lagi.

Setelah PR stabilisasi untuk tipe yang tidak pernah mendarat, kita dapat membuat PR stabilisasi (baru) untuk TryFrom / TryInto dan menggunakan rfcbot di sana untuk memastikan anggota tim melihatnya.

Apakah mungkin untuk mengubah tipe enum kosong yang ada seperti FromStringError menjadi alias menjadi ! di samping stabilisasi?

@clarcharr Ya. Karena koherensi impl sifat, kita hanya bisa melakukan ini untuk satu jenis. Untungnya, kami hanya memiliki std::string::ParseError . (Dan inilah tepatnya mengapa kami menggunakannya alih-alih menambahkan tipe baru untuk impl FromString for PathBuf di https://github.com/rust-lang/rust/pull/55148.)

Namun ini tidak terkait dengan TryFrom / TryInto . Ini topik untuk https://github.com/rust-lang/rust/issues/49691 / https://github.com/rust-lang/rust/issues/57012 , karena perlu terjadi dalam siklus rilis yang sama sebagai stabilisasi ! .

@SimonSapin Apakah mungkin untuk menggabungkan permintaan tarik saya (#56796) sebelum ini stabil?

Tentu, saya telah menambahkannya ke deskripsi masalah sehingga kami tidak kehilangan jejak.

Terima kasih!

Bisakah ini diintegrasikan ke dalam stabil? Ini adalah ketergantungan argdata untuk CloudABI.

@mcandre Ya. Ini sedang menunggu di https://github.com/rust-lang/rust/issues/57012 , yang baru-baru ini dibuka blokirnya. Saya berharap TryFrom akan berhasil di Rust 1,33 atau 1,34.

Saya muak menunggu ini dan saya punya waktu luang (akhirnya). Jika ada kode atau jenis dokumentasi yang bisa saya lakukan untuk mendorong ini ke depan, saya menawarkan diri untuk membantu.

56796 telah digabungkan. Jadi kita bisa memeriksanya.

@icefoxen Saya kira saat ini yang terbaik yang dapat Anda lakukan adalah memberikan umpan balik (dan perubahan) tentang dokumen sifat-sifat ini. Saat ini saya menemukan mereka sedikit kurang dibandingkan dengan sifat From dan Into .

Bekerja di dokumentasi. Masalah kecil di jalan: Saya ingin assert_eq!(some_value, std::num::TryFromIntError(())); . Namun, karena TryFromIntError tidak memiliki konstruktor dan tidak ada bidang publik, saya tidak dapat membuat turunannya. Adakah saran tentang cara melanjutkan? Untuk saat ini saya hanya melakukan assert!(some_value_result.is_err()); .

Maaf jika ini adalah tempat yang salah, tetapi sepertinya ada kesalahan dalam dokumen untuk TryFromIntError - ini mencantumkan impl Debug for TryFromIntError , padahal sebenarnya tidak ada kode untuk itu. Kompiler menghasilkan kesalahan saat ini ketika mencoba membuka hasil dari penggunaan TryFrom.

Apakah ini dimaksudkan untuk menjadi fmt::Debug impl?

@marco9999

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct TryFromIntError(());

Itu memiliki atribut #[derive(Debug)] .

Hmm ya ada... ada yang tidak berfungsi dengan baik?

error[E0599]: no method named `unwrap` found for type `std::result::Result<usize, <T as std::convert::TryInto<usize>>::Error>` in the current scope
  --> src\types\b8_memory_mapper.rs:67:51
   |
67 |         let address: usize = T::try_into(address).unwrap();
   |                                                   ^^^^^^
   |
   = note: the method `unwrap` exists but the following trait bounds were not satisfied:
           `<T as std::convert::TryInto<usize>>::Error : std::fmt::Debug`

@ marco9999 Anda mungkin melewatkan batasan umum. TryFromIntError hanya digunakan oleh beberapa jenis, tetapi T Anda dapat berupa apa saja:

fn foo<T: TryInto<u8>>(x: T) -> u8
where
    <T as TryInto<u8>>::Error: Debug,
{
    x.try_into().unwrap()
}

Bagaimanapun, ini sedikit keluar dari topik, maaf semuanya. IRC mungkin tempat yang lebih baik untuk mengajukan pertanyaan-pertanyaan ini.

Saya ingin assert_eq!(some_value, std::num::TryFromIntError(()));

@icefoxen Tidak ada nilai berguna yang terkait dengan TryFromIntError sehingga pernyataan seperti itu tampaknya tidak memiliki banyak nilai. Jika Anda memiliki Result<_, TryFromIntError> dan itu adalah Err , tidak ada nilai lain yang mungkin.

assert!(some_value_result.is_err());

Ini tampaknya masuk akal bagi saya.

Terima kasih @glaebhoerl.

Karena bug pemblokiran sedang diperbaiki (https://github.com/rust-lang/rust/issues/49593) saya berharap tipe never dapat distabilkan Soon® https://github.com/rust-lang/ rust/issues/57012 dan buka blokir ini. Namun masalah baru (https://github.com/rust-lang/rust/issues/57012#issuecomment-460740678) telah muncul, dan kami juga tidak memiliki konsensus tentang yang lain (https://github.com /rust-lang/rust/issues/57012#issuecomment-449098855).

Jadi dalam pertemuan libs minggu lalu saya mengemukakan kembali ide itu, saya percaya pertama kali diusulkan oleh @scottmcm di https://github.com/rust-lang/rust/issues/33417#issuecomment -299124605, untuk menstabilkan TryFrom dan TryInto tanpa tipe never, dan sebagai gantinya memiliki enum kosong yang nantinya bisa dijadikan alias untuk ! .

Terakhir kali kami membahas ini (https://github.com/rust-lang/rust/issues/33417#issuecomment-423069246), kami tidak dapat mengingat mengapa kami tidak melakukan ini sebelumnya.

Minggu lalu @dtolnay mengingatkan kami tentang masalah ini: sebelum ! menjadi tipe penuh, itu sudah dapat digunakan sebagai pengganti tipe kembalinya suatu fungsi untuk menunjukkan bahwa itu tidak pernah kembali. Ini termasuk tipe penunjuk fungsi. Jadi, dengan asumsi https://github.com/rust-lang/rust/pull/58302 mendarat di siklus ini, kode seperti ini akan valid di Rust 1.34.0:

use std::convert::Infallible;
trait MyTrait {}
impl MyTrait for fn() -> ! {}
impl MyTrait for fn() -> Infallible {}

Karena fn() -> ! dan fn() -> Infallible adalah dua tipe (penunjuk) yang berbeda, kedua impls tidak tumpang tindih. Tetapi jika kita mengganti enum kosong dengan alias tipe pub type Infallible = !; (ketika ! menjadi tipe penuh), maka kedua impls akan mulai tumpang tindih dan kode seperti di atas akan pecah.

Jadi mengubah enum menjadi alias akan menjadi perubahan besar. Pada prinsipnya kami tidak mengizinkannya di perpustakaan standar, tetapi dalam kasus ini kami merasa bahwa:

  • Seseorang harus keluar dari jalan mereka untuk membuat kode yang rusak oleh perubahan ini, jadi sepertinya tidak mungkin terjadi dalam praktik
  • Kami akan menggunakan Kawah untuk mendapatkan sinyal tambahan ketika saatnya tiba
  • Jika kita akhirnya menilai kerusakannya cukup signifikan, memiliki tipe never dan enum kosong dengan peran yang sama adalah inkonsistensi yang bisa kita jalani.

Berdasarkan diskusi ini, saya mengajukan https://github.com/rust-lang/rust/pull/58302 yang sekarang dalam Periode Komentar Akhir.

58015 harus siap untuk ditinjau/digabungkan sekarang.

@kennytm Apakah tidak mungkin untuk merujuk ke ! di stable? Misalnya, pertimbangkan hal berikut:

trait MyTrait {
    type Output;
}

impl<T> MyTrait for fn() -> T {
    type Output = T;
}

type Void = <fn() -> ! as MyTrait>::Output;

Setelah melakukan ini, Void mengacu pada tipe ! .

Itu terlihat seperti bug, yang berarti jaminan stabilitas tidak berlaku untuk itu. Menggunakan tipe never ( ! ) sebagai tipe dalam kapasitas apa pun masih tidak stabil, setidaknya hingga #57012 digabungkan.

Bagaimana saya bisa membantu dengan dokumentasi? :-)

Oh saya pikir https://github.com/rust-lang/rust/pull/58015 sudah mendarat, tapi belum… Mari kita bahas di sana.

Bisakah sifat TryFrom memiliki metode untuk memeriksa apakah argumen dapat dikonversi tanpa mengkonsumsinya?

fn check(value: &T) -> bool

Salah satu cara untuk bekerja dengan konversi tidak mungkin yang tidak dapat dikonsumsi adalah dengan mengembalikan nilai yang tidak dapat dikonversi bersama dengan kesalahan terkait.

Ups ini seharusnya ditutup oleh https://github.com/rust-lang/rust/pull/58302. Tutup sekarang.


@o01eg Cara umum untuk melakukan konversi non-konsumsi adalah dengan menerapkan misalnya TryFrom<&'_ Foo> alih-alih TryFrom<Foo> .

Tunggu...ini seharusnya tidak ditutup sampai mendarat pada hari Kamis yang stabil, kan?

Tidak, kami menutup masalah pelacakan saat PR yang menstabilkan fitur mendarat di master.

Tidak, kami biasanya menutup masalah pelacakan saat stabilisasi atau pemindahan mendarat di cabang master . Setelah itu tidak ada yang tersisa untuk dilacak. (Kecuali bug yang baru dilaporkan muncul, tetapi kami menanganinya secara terpisah.)

Masalah pelacakan ditutup oleh PR yang menstabilkannya. Tergantung pada siklus rilis, ini bisa memakan waktu hingga 12 minggu sebelum stable dirilis.

Mengerti. Terima kasih atas klarifikasinya, semuanya! :)

@gregdegruy perbarui versi Rust Anda ke 1,34 atau lebih tinggi.

Apakah halaman ini membantu?
0 / 5 - 0 peringkat