Masalah pelacakan untuk https://github.com/rust-lang/rfcs/pull/1542
Melakukan:
TryFrom
blanket impl untuk menggunakan Into
alih-alih From
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.
std
yang akan diimplementasikan?impl TryFrom<T> for T
?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 implFrom<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.
@SimonSapin https://github.com/rust-lang/rfcs/pull/1216
@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
danInto
ke tipe perpustakaan standar, saya berharap kita akan melakukan hal yang sama untukTryFrom
danTryInto
.
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 mengangkatimpl 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 padaTryFrom::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<...>
atauTryFrom<...>
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:
TryFrom<str>
di tanda tangan, yang dengan benar ada di modul konversi dan lebih jelas apa fungsinyaTryFrom<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:
SomeType::default()
SomeType::from(other)
diimplementasikan (Anda bisa mengetiknya dan melihat apakah itu dikompilasi, tanpa meraih dokumentasi)clone()
try_from
- oh tunggu: PSemua 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 Dari
untuk U mungkin harus menyiratkan TryFrom untukmu.
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 impl
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)
hanyaT::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:
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:
TryFrom
dimaksudkan untuk konversi yang salah _only_, jadi tidak memiliki hal-hal seperti u8 -> u128
atau usize -> usize
.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 memilikiimpl<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:
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)Infallible
sebagai Error
ketik di blanket impltype Infallible = !;
sebagai bagian dari menstabilkan tipe neverSaya 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 (misalnyatry!
)!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
atauTryFrom
meskipun memiliki banyak konversi, jadi bentuk terakhir sudah cukup untuk semua penggunaan saya dalam tipe yang saya definisikan. Saya pikir memiliki parameterTryInto<...>
atauTryFrom<...>
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>
danAsRef<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.
Jika ada yang mencari contoh cepat dari ini: https://play.rust-lang.org/?gist=bfc3de0696cbee0ed9640a3f60b33f5b&version=nightly
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:
string::ParseError
ke convert::Infallible
pub use
atau pub type
(apakah ini membuat perbedaan?)TryFrom
string::ParseError
dan convert::Infallible
dengan type _ = !
dan gunakan !
langsung di tempat mereka digunakan.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
dengantype 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:
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>
.
Kami sudah memiliki kedua impls ini:
https://doc.rust-lang.org/1.31.0/std/convert/trait.TryFrom.html#impl -TryFrom%3CU%3E
https://doc.rust-lang.org/1.31.0/std/convert/trait.TryInto.html#impl -TryInto%3CU%3E
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 dariInto<T> for U
, tetapiwhere 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).
@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.
@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.
Referensi silang: https://github.com/rust-lang/rust/pull/58302
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:
Berdasarkan diskusi ini, saya mengajukan https://github.com/rust-lang/rust/pull/58302 yang sekarang dalam Periode Komentar Akhir.
@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.
Komentar yang paling membantu
Saya ingin
!
menjadi tipe kosakata yang cukup sehinggaResult<_, !>
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.