Rust: Masalah pelacakan untuk operator `?` Dan pemblokiran `coba` (RFC 243, fitur` tanda_tanyakan` & `blok_coba`)

Dibuat pada 5 Feb 2016  ·  340Komentar  ·  Sumber: rust-lang/rust

Masalah pelacakan untuk rust-lang / rfcs # 243 dan rust-lang / rfcs # 1859.

Masalah implementasi:

  • [x] ? operator yang kira-kira sama dengan try! - # 31954
  • [x] try { ... } ekspresi - https://github.com/rust-lang/rust/issues/39849

    • [x] menyelesaikan pertanyaan sintaks do catch { ... }


    • [x] menentukan apakah blok tangkapan harus "membungkus" nilai hasil (pertama kali dibahas di https://github.com/rust-lang/rust/issues/41414, sekarang diselesaikan lagi di https://github.com/rust- lang / rust / issues / 70941)

    • [] Atasi masalah dengan jenis inferensi ( try { expr? }? saat ini memerlukan penjelasan jenis eksplisit di suatu tempat).

  • [x] menyelesaikan desain ciri Try (https://github.com/rust-lang/rfcs/pull/1859)

    • [x] menerapkan sifat baru Try (menggantikan Carrier ) dan mengubah ? untuk menggunakannya (https://github.com/rust-lang/rust/pull / 42275)

    • [x] menambahkan impls untuk Option dan seterusnya, dan kelompok pengujian yang sesuai (https://github.com/rust-lang/rust/pull/42526)

    • [x] menyempurnakan pesan error seperti yang dijelaskan di RFC (https://github.com/rust-lang/rust/issues/35946)

  • [x] pesan try dalam edisi baru
  • [x] blokir try{}catch (atau ident berikut lainnya) untuk membiarkan ruang desain terbuka untuk masa depan, dan arahkan orang-orang ke cara melakukan apa yang mereka inginkan dengan match sebagai gantinya
A-error-handling B-RFC-approved B-RFC-implemented B-unstable C-tracking-issue F-try_blocks Libs-Tracked T-lang T-libs

Komentar yang paling membantu

@ mark-im Saya rasa kita tidak dapat berpindah dari satu ke yang lain setelah stabilisasi. Seburuk yang saya anggap Ok-wrapping, tidak konsisten Ok-wrapping yang mencoba menebak apakah Anda menginginkannya atau tidak akan menjadi lebih buruk.

Semua 340 komentar

RFC yang menyertai membahas desugaring berdasarkan return / break berlabel, apakah kita mendapatkannya juga atau hanya akan ada perlakuan khusus untuk ? dan catch di kompiler?

EDIT: Saya pikir berlabel kembali / istirahat adalah ide bagus yang terpisah dari ? dan catch , jadi jika jawabannya tidak, saya mungkin akan membuka RFC terpisah untuk itu.

Pengembalian / istirahat berlabel murni untuk tujuan penjelasan.

Pada hari Jumat, 5 Feb 2016 jam 15.56, Jonathan Reem [email protected]
menulis:

RFC yang menyertai membahas desugaring berdasarkan pengembalian / istirahat berlabel,
apakah kita mendapatkannya juga atau akankah ada perlakuan khusus untuk itu? dan
menangkap di kompiler?

-
Balas email ini secara langsung atau lihat di GitHub
https://github.com/rust-lang/rust/issues/31436#issuecomment -180551605.

Pertanyaan lain yang belum terselesaikan yang harus kita selesaikan sebelum menstabilkan adalah kontrak apa yang harus dipatuhi impl s dari Into - atau apakah Into bahkan merupakan sifat yang tepat untuk digunakan untuk kesalahan-upcasting di sini. (Mungkin ini harus menjadi item daftar periksa lainnya?)

@em

Saya pikir berlabel kembali / istirahat adalah ide yang bagus ... Saya mungkin akan membuka RFC terpisah untuk itu.

Silakan lakukan!

Mengenai masalah sifat Carrier , berikut adalah contoh inti dari sifat yang saya tulis di awal proses RFC.
https://gist.github.com/thepowersgang/f0de63db1746114266d3

Bagaimana ini diperlakukan selama parsing?

struct catch {
    a: u8
}

fn main() {

    let b = 10;
    catch { a: b } // struct literal or catch expression with type ascription inside?

}

@petrochenkov Nah, definisi tidak dapat memengaruhi penguraian, tetapi saya pikir kita masih memiliki aturan lookahead, berdasarkan token kedua setelah { , : dalam kasus ini, jadi seharusnya tetap diurai sebagai struct literal.

Juga

let c1 = catch { a: 10 };
let c2 = catch { ..c1 }; // <--

struct catch {}
let c3 = catch {}; // <--

+ https://github.com/rust-lang/rfcs/issues/306 jika (kapan!) diterapkan.
Sepertinya tidak ada konflik selain struct literal.

Diberikan contoh di atas, saya untuk solusi paling sederhana (seperti biasa) - selalu perlakukan catch { dalam posisi ekspresi sebagai awal dari blok catch . Tidak ada yang menyebut struktur mereka catch .

Akan lebih mudah jika kata kunci digunakan daripada catch .

@bluss ya, saya akui tidak ada yang bagus ... override sepertinya satu-satunya yang dekat. Atau kita bisa menggunakan do , heh. Atau kombinasi, meskipun saya tidak langsung melihat yang bagus. do catch ?

do adalah satu-satunya yang tampaknya dekat IMO. Kata kunci soup dengan do sebagai awalan agak tidak beraturan, tidak mirip dengan bagian bahasa lainnya? Apakah while let merupakan kata kunci sup? Yang itu terasa baik-baik saja sekarang, saat Anda terbiasa.

port try! untuk menggunakan ?

Tidak bisakah ? -porting untuk menggunakan try! sebagai gantinya? Ini akan memungkinkan untuk kasus penggunaan di mana Anda ingin mendapatkan jalur pengembalian Result , misalnya saat debugging. Dengan try! ini cukup mudah, Anda cukup mengganti makro di awal file (atau di lib / main.rs):

macro_rules! try {
    ($expr:expr) => (match $expr {
        Result::Ok(val) => val,
        Result::Err(err) => {
            panic!("Error occured: {:?}", err)
        }
    })
}

Anda akan mendapatkan jejak tumpukan panik mulai dari kejadian pertama try! di jalur pengembalian Result . Faktanya, jika Anda melakukan try!(Err(sth)) jika Anda menemukan kesalahan alih-alih return Err(sth) , Anda bahkan mendapatkan jejak tumpukan lengkap.

Tetapi ketika men-debug pustaka asing yang ditulis oleh orang-orang yang belum menerapkan trik itu, seseorang mengandalkan penggunaan try! suatu tempat yang lebih tinggi dalam rantai. Dan sekarang, jika pustaka menggunakan operator ? dengan perilaku hardcode, mendapatkan stacktrace hampir mustahil.

Akan keren jika menimpa try! akan mempengaruhi operator ? juga.

Nanti ketika sistem makro mendapatkan lebih banyak fitur, Anda bahkan bisa panik! hanya untuk tipe tertentu.

Jika proposal ini memerlukan RFC, beri tahu saya.

Idealnya ? dapat diperpanjang untuk menyediakan dukungan pelacakan tumpukan secara langsung, daripada mengandalkan kemampuan untuk mengganti try! . Kemudian itu akan berhasil di mana saja.

Jejak tumpukan hanyalah satu contoh (meskipun menurut saya sangat berguna).
Jika sifat Carrier dibuat berfungsi, mungkin itu dapat mencakup ekstensi seperti itu?

Pada hari Minggu, 7 Feb 2016 jam 16.14, Russell Johnston [email protected]
menulis:

Idealnya? dapat diperpanjang untuk memberikan dukungan pelacakan tumpukan secara langsung,
daripada mengandalkan kemampuan untuk mengganti coba !. Maka itu akan berhasil
dimana mana.

-
Balas email ini secara langsung atau lihat di GitHub
https://github.com/rust-lang/rust/issues/31436#issuecomment -181118499.

Tanpa ingin berspekulasi, saya pikir itu bisa berhasil, meskipun dengan beberapa masalah.

Pertimbangkan kasus biasa di mana seseorang memiliki kode yang mengembalikan nilai Result<V,E> . Sekarang kita perlu mengizinkan beberapa implementasi dari sifat Carrier untuk hidup berdampingan. Agar tidak mengalami E0119 , seseorang harus membuat semua implementasi di luar ruang lingkup (mungkin melalui sifat berbeda yang secara default tidak diimpor), dan saat menggunakan operator ? , pengguna diharuskan untuk mengimpor penerapan.

Ini akan membutuhkan semua orang, bahkan mereka yang tidak ingin men-debug, untuk mengimpor implementasi sifat yang mereka inginkan saat menggunakan ? , tidak akan ada opsi untuk default yang telah ditentukan sebelumnya.

Mungkin E0117 juga bisa menjadi masalah jika ingin melakukan implementasi kustom Carrier untuk Result<V,E> , di mana semua tipe berada di luar batas, jadi libstd harus menyediakan satu set implementasi Carrier trait dengan kasus penggunaan yang paling sering digunakan (implementasi sepele, dan implementasi panic! ing, mungkin lebih).

Memiliki kemungkinan untuk mengganti melalui makro akan memberikan fleksibilitas yang lebih besar tanpa beban tambahan pada pelaksana asli (mereka tidak harus mengimpor implementasi yang mereka inginkan). Tetapi saya juga melihat bahwa rust tidak pernah memiliki operator berbasis makro sebelumnya, dan bahwa menerapkan ? melalui makro tidak mungkin dilakukan jika catch { ... } seharusnya berfungsi, setidaknya tidak tanpa item bahasa tambahan ( return_to_catch , throw , diberi label break dengan param seperti yang digunakan dalam RFC 243).

Saya baik-baik saja dengan pengaturan apa pun yang memungkinkan seseorang untuk mendapatkan Result stacktraces dengan jalur pengembalian Err , sementara hanya perlu memodifikasi sejumlah kecil kode, lebih disukai di bagian atas file. Solusi juga harus bekerja tidak terkait dengan bagaimana dan di mana tipe Err diimplementasikan.

Hanya untuk berpadu pada bikeshedding: catch in { ... } mengalir cukup baik.

catch! { ... } adalah pilihan backcompat lainnya.

Selain itu, saya tidak mengharapkan ini untuk mengubah apa pun, tetapi catatan bahwa ini akan merusak makro multi-lengan yang menerima $i:ident ? , dengan cara yang sama seperti jenis anggapan yang merusak $i:ident : $t:ty .

Jangan berlebihan dalam kompatibilitas mundur, cukup perlakukan catch sebagai kata kunci ketika diikuti oleh { (mungkin hanya dalam posisi ekspresi, tapi saya tidak yakin apakah itu banyak mengubah kompatibilitas-bijaksana).

Saya juga bisa membayangkan beberapa kemungkinan masalah yang tidak melibatkan struct literal (misalnya let catch = true; if catch {} ); tapi saya lebih suka perubahan kecil yang melanggar daripada sintaks yang lebih jelek.

Bukankah kita memiliki untuk menambahkan kata kunci baru? Kami dapat menawarkan beberapa jenis from __future__ opt-in untuk sintaks baru; atau tentukan nomor versi bahasa karat pada baris perintah / di Cargo.toml.
Saya sangat meragukan bahwa dalam jangka panjang, kami hanya dapat bekerja dengan kata kunci yang sudah dipesan. Kami tidak ingin kata kunci kami masing-masing memiliki tiga arti berbeda, bergantung pada konteksnya.

Saya setuju. Ini bahkan bukan RFC pertama di mana ini muncul (https://github.com/rust-lang/rfcs/pull/1444 adalah contoh lain). Saya berharap ini tidak akan menjadi yang terakhir. (Juga default dari https://github.com/rust-lang/rfcs/pull/1210, meskipun ini bukan RFC yang saya sukai.) Saya pikir kita perlu menemukan cara untuk menambahkan kata kunci jujur-to-god alih-alih mencoba mencari cara untuk meretas ad-hoc tata bahasa untuk setiap kasus baru.

Bukankah seluruh argumen untuk tidak mencadangkan beberapa kata kunci sebelum 1.0 bahwa kami pasti akan memperkenalkan cara untuk menambahkan kata kunci baru ke bahasa yang kompatibel secara mundur (mungkin dengan memilih secara eksplisit), jadi tidak ada gunanya? Sepertinya sekarang adalah saat yang tepat.

@japaric Apakah Anda tertarik untuk menghidupkan kembali PR lama Anda dan melakukan ini?

@aturon Implementasi saya hanya mendesain foo? dengan cara yang sama seperti try!(foo) . Ini juga hanya bekerja pada pemanggilan metode dan fungsi, yaitu foo.bar()? dan baz()? bekerja tetapi quux? dan (quux)? tidak. Apakah itu tidak masalah untuk implementasi awal?

@japaric Apa alasan untuk membatasinya pada pemanggilan metode dan fungsi? Bukankah menguraikannya lebih mudah sebagai operator postfix umum?

Apa alasan untuk membatasinya pada pemanggilan metode dan fungsi?

cara termudah (bagi saya) untuk menguji ekspansi ?

Bukankah menguraikannya lebih mudah sebagai operator postfix umum?

mungkin

@japaric Mungkin akan bagus untuk menggeneralisasikannya ke operator postfix lengkap (seperti yang disarankan @eddyb ), tapi tidak masalah untuk mendapatkan ? dengan desugaring sederhana dan kemudian menambahkan catch nanti.

@aturon Baiklah, saya akan melihat ke versi postfix minggu depan jika tidak ada yang mengalahkan saya untuk itu :-).

mendasarkan kembali / memperbarui PR saya di # 31954 :-)

Bagaimana dengan dukungan untuk menyediakan pelacakan tumpukan? Apakah itu direncanakan?

Saya benci menjadi pria +1, tetapi jejak tumpukan telah menghemat waktu saya di masa lalu. Mungkin di build debug, dan saat mencapai jalur error,? operator dapat menambahkan file / baris ke Vec di Result? Mungkin Vec juga hanya bisa di-debug?

Dan itu bisa diekspos atau diubah menjadi bagian dari deskripsi kesalahan ...

Saya terus mengalami situasi di mana saya ingin menggunakan try! / ? di dalam iterator yang mengembalikan Option<Result<T, E>> . Sayangnya saat ini tidak benar-benar berfungsi. Saya ingin tahu apakah sifat operator dapat kelebihan beban untuk mendukung ini atau apakah itu akan menjadi From lebih umum?

@ hexsel Saya benar-benar berharap tipe Result<> akan membawa banyak petunjuk instruksi dalam debug dan? akan menambahkannya. Dengan begitu, info DWARF dapat digunakan untuk membuat pelacakan tumpukan yang dapat dibaca.

@mitsuhiko Tapi bagaimana Anda bisa membuat dan mencocokkan pola Result ? Ini _hanya_ enum atm.

Adapun pembungkus Option , saya yakin Anda ingin Some(catch {...}) .

Saat ini, kebiasaan saya sekarang adalah melakukan try!(Err(bla)) alih-alih return Err() , sehingga saya dapat mengganti makro percobaan nanti dengan yang panik, untuk mendapatkan lacak balik. Ini berfungsi dengan baik untuk saya, tetapi kode yang saya tangani sangat rendah, dan sebagian besar berasal dari kesalahan. Saya masih harus menghindari ? jika saya menggunakan kode eksternal yang mengembalikan Result .

@eddyb itu akan membutuhkan dukungan bahasa untuk membawa nilai-nilai tersembunyi selain itu Anda perlu memanipulasi dengan cara lain. Saya bertanya-tanya apakah itu dapat dilakukan dengan cara lain tetapi saya tidak dapat melihat caranya. Satu-satunya cara lain adalah kotak kesalahan standar yang dapat memiliki data tambahan di dalamnya, tetapi tidak ada persyaratan untuk memiliki kotak kesalahan dan kebanyakan orang tidak melakukannya.

@mitsuhiko Saya dapat memikirkan metode (default) baru pada Error sifat dan TLS.
Yang terakhir ini kadang-kadang digunakan oleh pembersih.

@eddyb yang hanya berfungsi jika Hasil dapat diidentifikasi dan yang memerlukannya untuk dikotakkan atau akan berpindah-pindah dalam memori saat melewati tumpukan ke atas.

@mitsuhiko TLS? Tidak juga, Anda hanya perlu membandingkan kesalahan dengan nilai.

Atau bahkan hanya berdasarkan jenis (dengan menghubungkan From input dan output), berapa banyak kesalahan bersamaan yang Anda inginkan dari stacktrace harus disebarkan secara bersamaan?

Saya menentang penambahan Result -pengetasan khusus kompiler, secara pribadi, ketika solusi yang lebih sederhana bekerja.

@eddyb kesalahan melewati tumpukan. Yang Anda inginkan adalah EIP di setiap tingkat tumpukan, bukan hanya dari mana asalnya. Juga kesalahan adalah a) saat ini tidak dapat dibandingkan dan b) hanya karena mereka membandingkan sama tidak berarti mereka adalah kesalahan yang sama.

berapa banyak error serentak yang Anda inginkan dari stacktrace harus disebarkan secara bersamaan

Setiap kesalahan ditangkap dan ditampilkan kembali sebagai kesalahan yang berbeda.

Saya menentang menambahkan peretasan kompiler khusus Hasil, secara pribadi, ketika solusi yang lebih sederhana berfungsi.

Saya tidak melihat cara kerja solusi yang lebih sederhana tetapi mungkin saya melewatkan sesuatu di sana.

Anda dapat menyimpan penunjuk instruksi di setiap ? dan menghubungkannya dengan jenis kesalahan.
"Setiap kesalahan ditangkap dan ditampilkan kembali sebagai kesalahan yang berbeda." Tetapi bagaimana Anda akan menyimpan informasi itu jika disembunyikan di Result ?

Tapi bagaimana Anda akan menyimpan informasi itu jika disembunyikan di Result?

Anda tidak perlu menyimpan informasi itu di hasil. Yang perlu Anda simpan adalah ID unik untuk asal kegagalan sehingga Anda dapat menghubungkannya. Dan karena sifat kesalahan hanyalah sifat dan tidak memiliki penyimpanan, itu bisa disimpan dalam hasil. Penunjuk instruksi vec itu sendiri tidak harus disimpan dalam hasil, yang bisa pergi ke TLS.

Salah satu caranya adalah Anda memanggil metode failure_id(&self) pada hasil dan mengembalikan i64 / uuid atau sesuatu yang mengidentifikasi asal mula kegagalan.

Ini akan membutuhkan dukungan bahasa apa pun yang terjadi karena yang Anda butuhkan adalah karena hasilnya melewati tumpukan, kompilator memasukkan instruksi untuk merekam bingkai tumpukan yang dilewatinya. Jadi, kembalinya hasil akan terlihat berbeda dalam build debug.

"kompilator memasukkan instruksi untuk merekam tumpukan frame yang dilewatinya" - tetapi ? eksplisit, ini tidak seperti pengecualian, atau apakah Anda tidak suka merekam _hanya_ ? yang dilewatinya?

Bagaimanapun, jika Anda membongkar kesalahan secara manual dan kemudian memasukkannya kembali ke Err , bagaimana ID itu akan disimpan?

"Dan karena sifat kesalahan hanyalah sifat dan tidak memiliki penyimpanan, itu bisa disimpan dalam hasil sebagai gantinya"
Ada varian untuk ini: mengimplementasikan sifat Error bisa dikurung khusus di kompiler untuk menambahkan bidang integer tambahan ke tipe, membuat tipe akan memicu ID yang akan dibuat, dan salin / jatuhkan akan secara efektif menambah / mengurangi refcount (dan akhirnya menghapusnya dari "set kesalahan dalam penerbangan" TLS jika Result::unwrap tidak digunakan).

Tapi itu akan bertentangan dengan sifat Copy . Maksud saya, begitu juga rencana Anda, menambahkan perilaku khusus apa pun ke Result yang _not_ dipicu oleh ? atau tindakan pengguna tertentu lainnya dapat membatalkan invarian Copy .

EDIT : Pada titik ini Anda mungkin juga menyematkan Rc<ErrorTrace> di sana.
EDIT2 : Apa yang saya katakan, Anda dapat menghapus jejak kesalahan terkait pada catch .
EDIT3 : Sebenarnya, saat drop, lihat di bawah penjelasan yang lebih baik.

"kompilator memasukkan instruksi untuk merekam tumpukan frame yang dilewatinya" - tapi? eksplisit, ini tidak seperti pengecualian, atau Anda tidak suka merekam hanya? itu melewati?

Itu tidak berhasil karena ada terlalu banyak bingkai yang tidak dapat Anda gunakan dan tidak menggunakan ? . Jangankan bahwa tidak semua orang akan menangani kesalahan hanya dengan ? .

Bagaimanapun, jika Anda membongkar kesalahan secara manual dan kemudian memasukkannya kembali ke dalam Err, bagaimana ID itu akan disimpan?

Itu sebabnya harus ada dukungan kompilator. Kompilator harus melacak variabel lokal yang merupakan hasil dan melakukan yang terbaik untuk menyebarkan id hasil dan seterusnya untuk dibungkus ulang. Jika ini terlalu ajaib maka ini dapat dibatasi pada subset operasi.

Itu tidak berhasil karena ada terlalu banyak bingkai yang tidak dapat Anda gunakan dan tidak menggunakan ? . Jangankan bahwa tidak semua orang akan menangani kesalahan hanya dengan ? .

Oke, saya bisa melihat pengembalian Result secara langsung menjadi kasing khusus dalam fungsi kompleks dengan beberapa jalur pengembalian (beberapa di antaranya akan menjadi pengembalian awal dari ? ).

Jika ini terlalu ajaib maka ini dapat dibatasi pada subset operasi.

Atau dibuat sangat eksplisit. Apakah Anda memiliki contoh non- ? re-wrapping yang harus secara ajaib dilacak oleh kompilator?

@eddyb Kasus umum penanganan kesalahan secara manual adalah IoError di mana Anda ingin menangani subset:

loop {
  match establish_connection() {
    Ok(conn) => { ... },
    Err(err) => {
      if err.kind() == io::ErrorKind::ConnectionRefused {
        continue;
      } else {
        return Err(err);
      }
    }
  }
}

@mitsuhiko Maka menyimpan ID di dalam io::Error pasti akan berhasil.

Jadi untuk rekapitulasi, Vec<Option<Trace>> "sparse integer map" di TLS, dikunci oleh struct ErrorId(usize) dan diakses oleh item 3 lang:

  • fn trace_new(Location) -> ErrorId , saat membuat nilai kesalahan non- const
  • fn trace_return(ErrorId, Location) , tepat sebelum kembali dari fungsi _declared_ sebagai -> Result<...> (yaitu bukan fungsi generik saat _happens_ digunakan dengan tipe Result sana)
  • fn trace_destroy(ErrorId) , saat menghapus nilai kesalahan

Jika transformasi dilakukan pada MIR, Location dapat dihitung dari instruksi Span yang memicu konstruksi nilai kesalahan atau menulis ke Lvalue::Return , yang jauh lebih dapat diandalkan daripada penunjuk instruksi IMO (bagaimanapun tidak ada cara mudah untuk mendapatkannya di LLVM, Anda harus mengeluarkan asm inline untuk setiap platform tertentu).

@deddyb

Bukankah itu akan menyebabkan pembengkakan ukuran biner?

@ arielb1 Anda akan melakukannya dalam mode debug hanya di mana debuginfo membengkak biner - Anda juga dapat menggunakan kembali debuginfo dengan cerdik _shrug_.

@eddyb apa lokasi dalam kasus itu? Tidak yakin apa yang sulit tentang membaca IP itu. Tentu, Anda memerlukan JS khusus untuk setiap target tetapi itu tidak terlalu sulit.

@mitsuhiko bisa jadi info yang sama yang kita gunakan untuk pemeriksaan overflow dan kepanikan yang dipancarkan kompiler lainnya. Lihat core::panicking::panic .

Mengapa mengemas begitu banyak informasi tumpukan ke Error / Result sementara tumpukan dibatalkan? Mengapa tidak menjalankan kode saat tumpukan masih ada? Misalnya bagaimana jika Anda tertarik dengan variabel di jalur tumpukan? Cukup aktifkan orang untuk menjalankan kode ubahsuaian ketika Err dipanggil, misalnya untuk memanggil debugger pilihan mereka. Inilah yang telah disediakan try! karena merupakan makro (dapat diganti). Cara termudah adalah dengan panik pada kasus Err yang mencetak tumpukan asalkan seseorang telah memulai program dengan parameter yang tepat.

Dengan try Anda dapat melakukan apa pun yang Anda inginkan pada kasing Err , dan Anda dapat menimpa kotak makro yang lebar, atau sangat terbatas untuk tidak menyentuh kode penting kinerja, misalnya jika kesalahan sulit direproduksi dan Anda perlu menjalankan banyak kode penting kinerja.

Tidak ada yang perlu menyimpan sebagian kecil informasi dalam tumpukan buatan yang dibuat karena yang asli akan dihancurkan. Semua metode penggantian makro dapat ditingkatkan dengan adalah:

  • ? dapat diganti dengan cara yang sama, cara termudah adalah dengan mendefinisikan ? sebagai gula untuk try! - terutama diperlukan untuk menangkap kesalahan yang tidak berasal di perbatasan peti sebagai diuraikan dalam komentar saya di atas.
  • memiliki sistem makro yang lebih canggih, seperti mencocokkan jenis untuk memungkinkan lebih banyak fleksibilitas. Ya, seseorang dapat berpikir untuk memasukkan ini ke dalam sistem sifat tradisional, tetapi karat tidak memungkinkan penerapan sifat yang berlebihan, jadi itu akan menjadi sedikit rumit

@ est31 Tumpukan tidak _automatically_ "dibatalkan" seperti dalam keadaan panik, ini adalah gula sintaksis untuk pengembalian awal.
Dan ya, Anda dapat meminta compiler menyisipkan panggilan dari beberapa fungsi kosong dengan nama tetap yang dapat Anda gunakan untuk breakpoint, ini cukup mudah (dan juga memiliki informasi yang memungkinkan Anda melakukannya, misalnya call dump(data) - di mana dump dan data adalah argumen yang dapat dilihat debugger).

@eddyb Saya pikir fungsi kosong tidak akan mengizinkan kasus penggunaan, misalnya menyimpan beberapa contoh debug "kenari" pada penerapan besar untuk melihat apakah kesalahan muncul di log, kemudian kembali dan memperbaiki berbagai hal. Saya mengerti lebih baik menjadi proaktif daripada reaktif, tetapi tidak semuanya mudah diprediksi

@hexsel Ya, itulah sebabnya saya lebih memilih metode berbasis TLS mana Result::unwrap atau baru ignore metode (atau bahkan selalu ketika menjatuhkan kesalahan?) kesedihan jejak ke stderr.

@eddyb Jika Anda menambahkan penunjuk instruksi atau sesuatu yang diturunkan dari nilai ke tumpukan seperti struktur data di TLS maka Anda pada dasarnya membangun kembali versi kecil dari tumpukan kehidupan nyata. Pengembalian mengurangi tumpukan dengan satu entri. Jadi, jika Anda melakukannya sambil mengembalikan _unwind_ bagian dari tumpukan, sambil membangun versi terbatasnya di beberapa tempat lain di RAM. Mungkin "bersantai" adalah istilah yang salah untuk perilaku yang dihasilkan dari pengembalian "legal", tetapi jika semua kode tidak ? atau try! dan pada akhirnya Anda mencegat hasil akhirnya sama. Bagusnya karat tidak membuat kesalahan propagasi otomatis, saya sangat menyukai bagaimana java mengharuskan semua pengecualian untuk dicantumkan setelah kata kunci throws , karat adalah perbaikan yang bagus untuk itu.

@hexsel sebuah overriding (karena sudah ada sekarang untuk try! ) pendekatan berbasis akan memungkinkan untuk ini - Anda dapat menjalankan kode apa pun yang Anda inginkan, dan masuk ke sistem logging apa pun. Anda akan membutuhkan beberapa deteksi untuk "dupes" ketika beberapa try menangkap kesalahan yang sama saat ia menyebarkan tumpukan.

@ est31 Menimpa try! hanya berfungsi dalam kode Anda sendiri (secara harfiah berarti membayangi impor makro), harus sesuatu yang berbeda, seperti "item lang lemah" kami.

@ est31 Itu tidak benar-benar benar (tentang pelucutan), jejak dan tumpukan nyata tidak selalu memiliki hubungan apa pun, karena memindahkan Result s tidak harus naik dalam lacak balik asli, itu bisa pergi ke samping juga.
Juga, jika biner dioptimalkan, sebagian besar lacak balik hilang, dan jika Anda tidak memiliki info debug, maka Location benar-benar superior. Anda bahkan dapat menjalankan plugin obfuscating yang menggantikan semua informasi sumber dengan hash acak yang dapat dicocokkan oleh pengembang beberapa produk sumber tertutup.

Debugger berguna (dan sebagai tambahan, saya menyukai keluaran lacak balik yang lebih bersih lldb ), tetapi itu bukan obat mujarab, dan kami sudah mengeluarkan beberapa informasi tentang kepanikan sehingga Anda bisa mendapatkan beberapa petunjuk tentang apa yang sedang terjadi.

Tentang itu - Saya punya beberapa pemikiran tentang trik tingkat sistem ketik di mana {Option,Result}::unwrap akan memiliki argumen tipe tambahan, default ke tipe yang bergantung pada lokasi tempat fungsi itu dipanggil, sehingga kepanikan dari mereka akan memiliki jalan informasi lokasi yang lebih berguna.

Dengan kemajuan pada parametrization nilai, itu mungkin masih menjadi pilihan, tetapi jelas bukan satu-satunya, dan saya tidak ingin langsung mengabaikan Result traces, sebaliknya saya mencoba menemukan model yang _implementable_.

Mengesampingkan try! sama sekali bukan solusi karena ada di dalam peti Anda sendiri. Itu tidak dapat diterima sebagai pengalaman debugging. Saya sudah mencoba banyak dari itu mencoba berurusan dengan try! . Terutama jika Anda memiliki banyak peti yang terlibat dan kesalahan diubah beberapa kali dalam perjalanan melalui tumpukan, hampir tidak mungkin untuk mencari tahu dari mana kesalahan itu berasal dan mengapa. Jika bandaid seperti itu adalah solusinya maka kami harus meninjau kembali penanganan kesalahan secara umum untuk proyek Rust besar.

@eddyb jadi apakah saran Anda agar kami benar-benar menyematkan nama file, nama fungsi, nomor baris, dan nomor kolom ke vektor itu? Itu tampaknya sangat boros terutama karena informasi tersebut sudah terkandung dalam format yang jauh lebih dapat diproses di DWARF. DWARF juga memungkinkan kami menggunakan proses yang sama dengan harga terjangkau dalam produksi sedangkan info lokasi semacam ini tampaknya sangat boros sehingga tidak ada yang akan menjalankan biner rilis dengannya.

Bagaimana ini akan jauh lebih boros daripada informasi DWARF? Nama file akan dideduplikasi, dan pada x64 semuanya berukuran 3 petunjuk.

@mitsuhiko jadi pada dasarnya, Anda setuju dengan arahan umum, tetapi tidak dengan spesifikasi teknisnya?

Seberapa mudah untuk mengekspos informasi DWARF ke Rust API umum?

@eddyb karena informasi DWARF tidak terdapat dalam rilis biner tetapi file terpisah. Jadi saya dapat memiliki file debug di server simbol dan mengirimkan biner yang dilucuti kepada pengguna.

@mitsuhiko Oh, itu pendekatan yang sama sekali berbeda dari yang saya asumsikan. Rust saat ini tidak mendukung atm itu, AFAIK, tapi saya setuju itu harus.

Apakah menurut Anda pointer instruksi sebenarnya berguna dibandingkan dengan pengidentifikasi acak yang dihasilkan oleh sistem build Anda untuk tujuan debugging rilis binari?

Pengalaman saya adalah bahwa dengan debugger inlining mana pun mengalami kesulitan memulihkan banyak jejak tumpukan, kecuali untuk rekursi diri / timbal balik eksplisit dan fungsi yang sangat besar.

Ya, try! ada di dalam peti Anda. Jika Anda meneruskan penunjuk fungsi atau yang serupa dengan kode perpustakaan, dan ada kesalahan dalam fungsi Anda, pendekatan coba akan tetap berfungsi. Jika peti yang Anda gunakan memiliki kesalahan atau bug internal, Anda mungkin memerlukan kode sumbernya untuk melakukan debug, jika informasi Err dikembalikan tidak membantu. Pelacakan tumpukan hanya berguna jika Anda memiliki akses ke kode, jadi pustaka sumber tertutup (yang kodenya tidak dapat Anda ubah) harus pergi ke dukungan dengan satu atau lain cara.

Bagaimana dengan mengaktifkan kedua pendekatan, dan membiarkan pengembang memutuskan yang terbaik untuk mereka? Saya tidak menyangkal bahwa pendekatan berbasis TLS tidak memiliki keuntungan apapun.

Model percobaan dapat diimplementasikan dengan sangat mudah, cukup desugar ? menjadi try , hanya ekstensi bahasa tambahan yang diperlukan jika catch masuk (Anda akan menambahkan perilaku itu di dalam perilaku hardcode dari ? )

@eddyb "Apakah menurut Anda pointer instruksi sebenarnya berguna dibandingkan dengan pengidentifikasi acak yang dihasilkan oleh sistem build Anda untuk keperluan debugging rilis binari?"

Begitulah cara kerja debugging biner native secara umum. Kami (Sentry) menggunakannya hampir seluruhnya untuk Dukungan iOS pada saat ini. Kami mendapatkan crash dump dan menyelesaikan alamat dengan llvm-symbolizer ke simbol yang sebenarnya.

@mitsuhiko Karena kita sudah menyematkan libbacktrace , kita dapat menggunakannya untuk menyelesaikan penunjuk kode ke lokasi sumber, jadi saya tidak sepenuhnya menentangnya.

@eddyb ya. Baru saja melihat kode panik. Akan lebih baik jika itu sebenarnya adalah API yang dapat digunakan oleh kode Rust. Saya dapat melihat manfaat ini di beberapa tempat lagi.

Tentang itu - Saya punya beberapa pemikiran tentang trik tingkat sistem ketik di mana {Option, Result} :: unwrap akan memiliki argumen tipe tambahan, default ke tipe yang bergantung pada lokasi tempat fungsi itu dipanggil, sehingga kepanikan dari mereka akan memiliki informasi lokasi yang jauh lebih berguna.

Ngomong-ngomong soal...

@glaebhoerl Hah, mungkin itu layak dikejar? Setidaknya sebagai eksperimen yang tidak stabil.

@eddyb Tidak tahu. Mungkin layak untuk membahasnya dengan beberapa GHCers yang terlibat dengannya terlebih dahulu, saya hanya mendengar tentang ini secara sepintas dan mencari tautannya di Google sekarang. Dan Rust tidak memiliki parameter implisit yang sebenarnya seperti yang dilakukan GHC; apakah parameter tipe default dapat berfungsi atau tidak adalah pertanyaan yang menarik (mungkin pertanyaan yang lebih Anda pikirkan daripada saya).

Hanya pemikiran: akan berguna untuk memiliki sakelar yang menyebabkan rustc menjadi kasus khusus yang membuat Err sedemikian rupa sehingga memanggil fungsi fn(TypeId, *mut ()) dengan payload sebelum mengembalikan . Itu seharusnya cukup untuk memulai dengan hal-hal dasar seperti memfilter kesalahan berdasarkan muatan, menjebak ke dalam debugger jika melihat kesalahan yang diinginkan, atau menangkap jejak balik untuk jenis kesalahan tertentu.

PR 33389 menambahkan dukungan eksperimental untuk sifat Carrier . Karena ini bukan bagian dari RFC asli, itu harus mendapatkan periode pemeriksaan dan diskusi yang sangat dekat sebelum kita pindah ke FCP (yang mungkin harus terpisah ke FCP untuk sisa operator ? ). Lihat utas diskusi ini untuk lebih jelasnya.

Saya menentang untuk memperpanjang ? menjadi Option s.

Kata-kata di RFC cukup jelas tentang fakta bahwa operator ? adalah tentang menyebarkan kesalahan / "pengecualian".
Menggunakan Option untuk melaporkan kesalahan adalah alat yang salah. Mengembalikan None adalah bagian dari alur kerja normal program yang berhasil, sedangkan mengembalikan Err selalu menunjukkan kesalahan.

Jika kita ingin memperbaiki beberapa area penanganan kesalahan (misalnya dengan menambahkan jejak tumpukan), menerapkan ? pada Option berarti kita harus mengecualikan ? dari perubahan .

@tomaka bisakah kita terus diskusi di utas diskusi ? (Saya telah meringkas keberatan Anda ) Secara pribadi saya menemukan bahwa diskusi panjang tentang GH menjadi sangat berat, dan alangkah baiknya juga dapat memisahkan diskusi tentang poin khusus ini dari poin atau pertanyaan lain di masa mendatang yang mungkin muncul.

@eddyb Berikut adalah dokumen versi rilis dari fitur GHC callstacks implisit yang saya sebutkan sebelumnya .

Tidak ada pembaruan di sini untuk beberapa saat. Apakah ada yang bekerja untuk memajukan ini? Apakah sisa tugas di OP masih akurat? Adakah yang bisa diinginkan di sini?

Saya bermain-main akhir pekan lalu jika memungkinkan untuk menulis klien Sentry untuk Rust yang masuk akal. Karena kebanyakan penanganan kesalahan sebenarnya didasarkan pada hasil sekarang, bukan kepanikan, saya perhatikan bahwa kegunaan ini sangat terbatas pada titik di mana saya memutuskan untuk meninggalkan ini sepenuhnya.

Saya pergi ke basis kode crates.io sebagai contoh untuk mencoba mengintegrasikan sistem pelaporan kesalahan ke dalamnya. Ini membawa saya kembali ke RFC ini karena saya benar-benar berpikir bahwa kecuali kita dapat merekam penunjuk instruksi entah bagaimana karena hasilnya diturunkan dan diubah dalam stacktraces yang berbeda, tidak mungkin untuk mendapatkan pelaporan kesalahan yang tepat. Saya sudah melihat ini menjadi rasa sakit yang luar biasa hanya dengan men-debug kegagalan logika kompleks lokal di mana saya saat ini menggunakan kepanikan di mana saya pikir_ kesalahan itu berasal.

Sayangnya, saat ini saya tidak melihat bagaimana IP dapat direkam tanpa perubahan besar pada cara kerja hasil. Apakah ada orang lain yang pernah bermain-main dengan ide ini sebelumnya?

Kami membahas hal ini dalam pertemuan @ rust-lang / lang. Beberapa hal yang muncul:

Pertama, ada minat yang pasti untuk melihat ? stabil secepat mungkin. Namun, saya pikir sebagian besar dari kita juga ingin melihat ? beroperasi pada Option dan bukan hanya Result (tapi tidak, menurut saya, bool , karena juga telah diusulkan). Satu kekhawatiran tentang stabilisasi adalah bahwa jika kita menstabilkan tanpa menawarkan sifat apa pun yang akan mengizinkan jenis selain Result untuk digunakan, maka itu tidak kompatibel ke belakang untuk menambahkannya nanti.

Misalnya, saya sendiri menulis kode seperti ini dengan beberapa keteraturan:

let v: Vec<_> = try!(foo.iter().map(|x| x.to_result()).collect());

di mana saya mengandalkan try! untuk menginformasikan jenis inferensi bahwa saya mengharapkan collect untuk mengembalikan Result<Vec<_>, _> . Jika akan menggunakan ? , inferensi _might_ ini gagal di masa mendatang.

Namun, dalam diskusi sebelumnya kami juga memutuskan bahwa amandemen RFC diperlukan untuk membahas poin-poin penting dari segala jenis sifat "pembawa". Jelas RFC ini harus ditulis SECEPATNYA, tetapi kami memilih untuk tidak memblokir kemajuan ? dalam diskusi itu.

Satu pemikiran yang kami miliki adalah bahwa jika kami mengambil implementasi @nrc dan kami membuat sifat tidak stabil dan menerapkannya hanya untuk Result dan beberapa tipe dummy privat, yang seharusnya menahan inferensi sementara tetap hanya menghasilkan ? dapat digunakan dengan Result .

Satu poin terakhir: Saya pikir sebagian besar dari kita akan lebih suka jika Anda menggunakan ? pada nilai Option , ini mensyaratkan bahwa jenis kembalian fungsi Anda juga harus Option (bukan mis. Result<T,()> ). Menarik untuk dicatat bahwa ini akan membantu dengan batasan inferensi, karena pada akhirnya kita dapat menyimpulkan dari tipe pengembalian yang dideklarasikan dalam banyak kasus tipe apa yang diperlukan.

Alasan untuk tidak menginginkan inter-konversi adalah karena hal itu tampaknya mengarah pada logika yang longgar, semacam analogi dengan bagaimana C mengizinkan if x bahkan ketika x memiliki tipe integral. Artinya, jika Option<T> menunjukkan nilai di mana None adalah bagian dari domain dari nilai itu (sebagaimana mestinya), dan Result<> mewakili (biasanya) kegagalan fungsi agar berhasil, maka anggaplah bahwa None berarti fungsi tersebut harus error karena mencurigakan (dan seperti semacam konvensi arbitrer). Tapi diskusi itu bisa menunggu RFC, kurasa.

Alasan untuk tidak menginginkan antar-konversi adalah karena tampaknya akan mengarah pada logika yang longgar, semacam analogi dengan bagaimana C mengizinkan if x bahkan ketika x memiliki tipe integral. Artinya, jika Option<T> menunjukkan nilai di mana None adalah bagian dari domain dari nilai tersebut (sebagaimana mestinya), dan Result<> mewakili (biasanya) kegagalan fungsi agar berhasil, maka anggaplah bahwa None berarti fungsi tersebut harus error karena tampaknya mencurigakan (dan seperti semacam konvensi arbitrer). Tapi diskusi itu bisa menunggu RFC, kurasa.

Saya sangat setuju dengan ini.

Pertanyaan lain yang telah kami sepakati untuk stabilisasi gerbang adalah memaku kontrak yang harus dipatuhi impl dari sifat From (atau sifat apa pun yang akhirnya kami gunakan untuk Err -upcasting ).

@glaeberl

Pertanyaan lain yang telah kami sepakati untuk stabilisasi gerbang adalah memaku kontrak yang menyiratkan sifat Dari yang harus dipatuhi (atau sifat apa pun yang akhirnya kami gunakan untuk upcasting-Err).

Memang. Dapatkah Anda menyegarkan ingatan saya dan memulai dengan beberapa contoh hal yang menurut Anda harus dilarang? Atau mungkin hanya hukum yang ada dalam pikiran Anda? Saya harus mengakui bahwa saya waspada terhadap "hukum" seperti ini. Untuk satu hal, mereka memiliki kecenderungan untuk diabaikan dalam praktiknya - orang mengambil keuntungan dari perilaku sebenarnya ketika itu sesuai dengan tujuan mereka, bahkan jika itu melampaui batasan yang dimaksudkan. Jadi itu mengarah ke satu pertanyaan lain: jika kita memiliki hukum, apakah kita akan menggunakannya untuk apa pun? Optimasi? (Sepertinya tidak mungkin bagi saya.)

Ngomong-ngomong, bagaimana status ekspresi catch ? Apakah mereka diterapkan?

Sayangnya tidak :(

Pada Selasa, 26 Juli 2016 pukul 06:41:44 -0700, Alexander Bulaev menulis:

Ngomong-ngomong, bagaimana status ekspresi catch ? Apakah mereka diterapkan?


Anda menerima ini karena Anda yang membuat utas.
Balas email ini secara langsung atau lihat di GitHub:
https://github.com/rust-lang/rust/issues/31436#issuecomment -235270663

Mungkin Anda harus merencanakan penerapannya? Tidak ada yang lebih menyedihkan daripada RFC yang diterima tetapi tidak diimplementasikan ...

cc # 35056

FYI, https://github.com/rust-lang/rfcs/pull/1450 (tipe untuk varian enum) akan membuka beberapa cara menarik untuk mengimplementasikan Carrier . Misalnya, sesuatu seperti:

trait Carrier {
    type Success: CarrierSuccess;
    type Error: CarrierError;
}

trait CarrierSuccess {
    type Value;
    fn into_value(self) -> Self::Value;
}

// (could really use some HKT...)
trait CarrierError<Equivalent: CarrierError> {
    fn convert_error(self) -> Equivalent;
}

impl<T, E> Carrier for Result<T, E>
{
    type Success = Result::Ok<T, E>;
    type Error = Result::Err<T, E>;
}

impl<T, E> CarrierSuccess for Result::Ok<T, E> {
    type Value = T;
    fn into_value(self) -> Self::Value {
        self.0
    }
}

impl<T, E1, E2> CarrierError<Result::Err<T, E2>> for Result::Err<T, E1>
    where E2: From<E1>,
{
    fn convert_error(self) -> Result::Err<T, E2> {
        Err(self.into())
    }
}

impl<T> Carrier for Option<T>
{
    type Success = Option::Some<T>;
    type Error = None;
}

impl<T> CarrierSuccess for Option::Some<T> {
    type Value = T;
    fn into_value(self) -> Self::Value {
        self.0
    }
}

impl<T> CarrierError<Option::None> for Option::None {
    fn convert_error(self) -> Option::None {
        self
    }
}

fn main() {
    let value = match might_be_err() {
        ok @ Carrier::Success => ok.into_value(),
        err @ Carrier::Error => return err.convert_error(),
    }
}

Saya hanya ingin memposting silang beberapa pemikiran dari https://github.com/rust-lang/rust/pull/35056#issuecomment -240129923. PR itu memperkenalkan sifat Carrier dengan tipe dummy. Tujuannya adalah untuk melindungi - khususnya, kami ingin menstabilkan ? tanpa harus menstabilkan interaksinya dengan jenis inferensi. Kombinasi sifat dan tipe dummy ini sepertinya akan sangat konservatif.

Idenya adalah (saya pikir) bahwa kami kemudian akan menulis RFC yang membahas Carrier dan mencoba memodifikasi desain agar sesuai, menstabilkan hanya ketika kami senang dengan bentuk keseluruhan (atau mungkin menghapus Carrier sama sekali, jika kita tidak dapat mencapai desain yang kita suka).

Sekarang, berbicara sedikit lebih spekulatif, saya _mengantisipasi_ bahwa, jika kita mengadopsi sifat Carrier , kita ingin melarang interkonversi antar operator (sedangkan sifat ini pada dasarnya adalah cara untuk mengubah ke / dari Result ). Jadi secara intuitif jika Anda menerapkan ? ke Option , tidak masalah jika fn mengembalikan Option ; dan jika Anda menerapkan ? ke Result<T,E> , tidak masalah jika fn mengembalikan Result<U,F> mana E: Into<F> ; tetapi jika Anda menerapkan ? ke Option dan fn mengembalikan Result<..> , itu tidak baik.

Meskipun demikian, aturan semacam ini sulit diungkapkan dalam sistem tipe saat ini. Titik awal yang paling jelas adalah sesuatu seperti HKT (yang tentu saja tidak benar-benar kita miliki, tapi mari kita abaikan untuk saat ini). Namun, itu belum tentu sempurna. Jika kita menggunakannya, orang akan berasumsi bahwa parameter Self untuk Carrier memiliki jenis type -> type -> type , karena Result dapat mengimplementasikan Carrier . Itu akan memungkinkan kami untuk mengekspresikan hal-hal seperti Self<T,E> -> Self<U,F> . Namun, _tidak_ harus berlaku untuk Option , yang memiliki jenis type -> type (semua ini tentu saja akan bergantung pada jenis sistem HKT yang kita adopsi, tapi saya rasa kita tidak ' Akan langsung ke "lambda tipe umum"). Yang lebih ekstrim lagi mungkin tipe seperti bool (walaupun saya tidak ingin menerapkan Carrier untuk bool, saya berharap beberapa orang mungkin ingin menerapkan Carrier untuk tipe baru bool).

Apa yang saya pertimbangkan adalah bahwa aturan pengetikan untuk ? itu sendiri bisa menjadi khusus: misalnya, kita mungkin mengatakan bahwa ? hanya dapat diterapkan ke tipe nominal Foo<..> dari _some_ kind, dan itu akan cocok dengan sifat Carrier terhadap tipe ini, tetapi itu akan mensyaratkan bahwa tipe kembalian dari fn penutup juga Foo<..> . Jadi pada dasarnya kita akan memberi contoh Foo dengan parameter tipe baru. Kelemahan dari ide ini adalah jika bukan tipe ? yang sedang diterapkan atau tipe dari fn yang melampirkan tidak diketahui, kita tidak dapat memaksakan batasan ini tanpa menambahkan beberapa jenis baru dari sifat kewajiban. Ini juga agak ad-hoc. :) Tapi itu akan berhasil.

Pemikiran lain yang saya miliki adalah bahwa kita mungkin mempertimbangkan kembali sifat Carrier . Idenya adalah memiliki Expr: Carrier<Return> mana Expr adalah tipe ? diterapkan dan Return adalah tipe lingkungan. Misalnya, mungkin akan terlihat seperti ini:

trait Carrier<Target> {
    type Ok;
    fn is_ok(&self) -> bool; // if true, represents the "ok-like" variant
    fn unwrap_into_ok(self) -> Self::Ok; // may panic if not ok
    fn unwrap_into_error(self) -> Target; // may panic if not error
}

Kemudian expr? desugars ke:

let val = expr;
if Carrier::is_ok(&val) {
    val.unwrap_into_ok()
} else {
    return val.unwrap_into_error();
}

Perbedaan utama di sini adalah bahwa Target bukanlah tipe _error_, tetapi tipe Result . Jadi misalnya kita bisa menambahkan impl berikut:

impl<T,U,E,F> Carrier<Result<U,F>> for Result<T,E>
    where E: Into<F>
{
    type Ok = T;
    fn is_ok(&self) -> bool { self.is_ok() }
    fn unwrap_into_ok(self) -> Self::Ok { self.unwrap() }
    fn unwrap_into_error(self) -> { Err(F::from(self.unwrap_err())) }
}

Dan kemudian kami dapat menambahkan:

impl<T> Carrier<Option<T>> for Option<T> {
    type Ok = T;
    fn is_ok(&self) -> bool { self.is_some() }
    fn unwrap_into_ok(self) -> Self::Ok { self.unwrap() }
    fn unwrap_into_error(self) -> { debug_assert!(self.is_none()); None }
}

Dan akhirnya kita bisa menerapkan untuk bool seperti ini:

struct MyBool(bool);
impl<T> Carrier<MyBool> for MyBool {
    type Ok = ();
    fn is_ok(&self) -> bool { self.0 }
    fn unwrap_into_ok(self) -> Self::Ok { debug_assert!(self.0); () }
    fn unwrap_into_error(self) -> { debug_assert!(!self.0); self }
}

Sekarang versi ini lebih fleksibel. Misalnya, kami _dapat_ mengizinkan interkonversi antara nilai Option untuk diubah menjadi Result dengan menambahkan impl seperti:

impl<T> Carrier<Result<T,()>> for Option<T> { ... }

Tapi tentu saja kita tidak perlu (dan tidak akan).

@Tokopedia

FYI, rust-lang / rfcs # 1450 (jenis untuk varian enum) akan membuka beberapa cara menarik untuk mengimplementasikan Carrier

Saat saya menulis ide yang baru saja saya tulis, saya berpikir tentang memiliki tipe untuk varian enum, dan bagaimana hal itu dapat memengaruhi banyak hal.

Satu hal yang saya perhatikan ketika menulis beberapa kode yang menggunakan ? adalah agak menjengkelkan untuk tidak memiliki kata kunci "lempar" apa pun - khususnya, jika Anda menulis Err(foo)? , kompiler tidak ' t _tahu_ bahwa ini akan kembali, jadi Anda harus menulis return Err(foo) . Tidak apa-apa, tapi kemudian Anda tidak mendapatkan konversi into() tanpa menuliskannya sendiri.

Ini muncul dalam kasus seperti:

let value = if something_or_other() { foo } else { return Err(bar) };

Oh, saya harus menambahkan satu hal lagi. Fakta bahwa kita mengizinkan impls untuk mempengaruhi jenis inferensi _should_ berarti bahwa foo.iter().map().collect()? , dalam konteks di mana fn mengembalikan Result<..> , saya curiga tidak ada anotasi jenis yang diperlukan, karena jika kita tahu bahwa Jenis pengembalian fn adalah Result , hanya satu impl yang berpotensi diterapkan (setidaknya secara lokal).

Oh, dan, versi yang sedikit lebih baik dari sifat Carrier mungkin adalah:

trait Carrier<Target> {
    type Ok;
    fn into_carrier(self) -> Result<Self::Ok, Target>;
}

di mana Anda akan menerapkannya seperti:

impl<T,U,E,F> Carrier<Result<U,F>> for Result<T,E>
    where E: Into<F>
{
    type Ok = T;
    fn into_carrier(self) -> Result<T, Result<U,F>> {
        match self { Ok(v) => Ok(v), Err(e) => Err(e.into()) }
    }
}

Dan expr? akan menghasilkan kode seperti:

match Carrier::into_carrier(expr) {
    Ok(v) => v,
    Err(e) => return e,
}

Sisi negatifnya (atau terbalik ...) dari hal ini tentu saja adalah bahwa konversi Into didorong ke impls, yang berarti bahwa orang mungkin tidak menggunakannya ketika itu masuk akal. Tetapi juga berarti Anda dapat menonaktifkannya jika (untuk tipe khusus Anda) yang tidak diinginkan.

@nikomatsakis IMO, sifatnya harus IntoCarrier dan IntoCarrier::into_carrier harus mengembalikan Carrier (enum baru) daripada menggunakan kembali hasil seperti ini. Itu adalah:

enum Carrier<C, R> {
    Continue(C),
    Return(R),
}
trait IntoCarrier<Return> {
    type Continue;
    fn into_carrier(self) -> Carrier<Self::Continue, Return>;
}

@Stebalien yakin, sepertinya baik-baik saja.

Nominasi untuk diskusi (dan kemungkinan FCP untuk operator ? saja) pada rapat tim lang. Saya berasumsi kita perlu mendapatkan semacam sifat Pengangkut sementara dalam beberapa hari ke depan ke FCP.

Saya membuka rust-lang / rfcs # 1718 untuk membahas sifat Pengangkut.

Dengarlah kamu, dengar kamu! Operator ? secara khusus sekarang memasuki periode komentar terakhir . Diskusi ini berlangsung kira-kira untuk siklus rilis ini yang dimulai pada tanggal 18 Agustus. Kecenderungannya adalah untuk menstabilkan operator ? .

Sehubungan dengan sifat pembawa , versi sementara mendarat di # 35777 yang harus memastikan bahwa kita memiliki kebebasan untuk memutuskan salah satu cara dengan mencegah interaksi yang tidak diinginkan dengan jenis inferensi.

Anggota @ rust-lang / lang, harap centang nama Anda untuk menandai persetujuan. Tinggalkan komentar dengan kekhawatiran atau keberatan. Lainnya, silakan tinggalkan komentar. Terima kasih!

  • [x] @nikomatsakis
  • [x] @nrc
  • [x] @aturon
  • [x] @eddyb
  • [] @pnkfelix (sedang liburan)

Saya ingin tahu apakah perpustakaan berbasis tokio pada akhirnya akan menggunakan and_then banyak. Itu akan menjadi satu argumen untuk membiarkan foo().?bar() menjadi singkatan dari foo().and_then(move |v| v.bar()) , sehingga Hasil dan Masa Depan dapat menggunakan notasi yang sama.

Untuk memperjelas, FCP ini adalah tentang fitur question_mark , bukan menangkap , bukan? Judul masalah ini menyiratkan pelacakan kedua fitur tersebut dalam masalah yang satu ini.

@seanmonstar yang terakhir ini bahkan belum diterapkan, jadi, ya. Agaknya jika FCP menghasilkan penerimaan, ini akan diubah menjadi track catch .

Untuk memperjelas, FCP ini adalah tentang fitur question_mark, tidak menangkap, bukan? Judul masalah ini menyiratkan pelacakan kedua fitur tersebut dalam masalah yang satu ini.

Ya, hanya fitur question_mark .

Tindak lanjut di https://github.com/rust-lang/rfcs/issues/1718#issuecomment -241764523. Saya mengira perilaku saat ini ? dapat digeneralisasikan menjadi "bind current continuation", tetapi bagian map_err(From::from) dari ? membuatnya sedikit lebih dari sekadar bind: /. Jika kita menambahkan instance From untuk hasil seluruhnya, maka saya kira semantiknya bisa menjadi v? => from(v.bind(current-continuation)) .

Ada banyak diskusi tentang manfaat relatif ? di utas internal ini:

https://internals.rust-lang.org/t/the-operator-will-be-harmful-to-rust/3882/

Saya tidak punya waktu untuk membuat ringkasan yang dalam sekarang. Perenungan saya adalah bahwa komentar difokuskan pada pertanyaan apakah ? cukup terlihat untuk menarik perhatian pada kesalahan, tetapi saya mungkin mengabaikan aspek lain dari diskusi. Jika orang lain punya waktu untuk meringkas, itu bagus!

Saya belum berkomentar sebelumnya dan ini mungkin sudah terlambat, tetapi saya menemukan operator ? membingungkan, jika digunakan sebagai pernyataan pengembalian tersembunyi, seperti yang ditunjukkan @hauleth dalam diskusi yang Anda

Dengan try! , jelas bahwa mungkin ada pengembalian di suatu tempat, karena makro dapat melakukannya. Dengan ? sebagai return tersembunyi, kita akan memiliki 3 cara untuk mengembalikan nilai dari suatu fungsi:

  • pengembalian implisit
  • pengembalian eksplisit
  • ?

Saya memang suka ini, seperti yang dikatakan @CryZe :

Dengan cara ini familiar bagi semua orang, itu menyalurkan kesalahan sampai akhir, di mana Anda bisa menanganinya, dan tidak ada pengembalian implisit. Jadi kira-kira bisa terlihat seperti ini:

biarkan a = coba! (x? .y? .z);

Itu membantu membuat kode lebih ringkas dan tidak menyembunyikan pengembalian. Dan itu familiar dari bahasa lain seperti coffeescript .

Bagaimana membuat ? menyelesaikan pada tingkat ekspresi dan bukan pada tingkat fungsi mempengaruhi masa depan? Untuk semua kasus penggunaan lainnya, tampaknya tidak masalah bagi saya.

Saya memang suka ini, seperti yang dikatakan @CryZe :

Dengan cara ini familiar bagi semua orang, itu menyalurkan kesalahan sampai akhir, di mana Anda bisa menanganinya, dan tidak ada pengembalian implisit. Jadi kira-kira bisa terlihat seperti ini:

biarkan a = coba! (x? .y? .z);

Saya telah mendalilkan ini. Saya pikir ini akan menjadi solusi yang tepat.

Dengan try !, jelas bahwa mungkin ada pengembalian di suatu tempat, karena makro dapat melakukannya.

Ini hanya jelas karena Anda terbiasa dengan cara kerja makro di Rust. Yang akan sama persis setelah ? stabil, tersebar luas, dan dijelaskan di setiap pengantar Rust.

@tokopedia

kita akan memiliki 3 cara untuk mengembalikan nilai dari suatu fungsi:

  • pengembalian implisit

Rust tidak memiliki "pengembalian implisit", ia memiliki ekspresi yang mengevaluasi suatu nilai. Ini adalah perbedaan penting.

if foo {
    5
}

7

Jika Rust memiliki "implicit return", kode ini akan dikompilasi. Tetapi tidak, Anda membutuhkan return 5 .

Mana yang akan sama persis sekali? stabil, tersebar luas, dan dijelaskan di setiap pengantar Rust.

Untuk contoh tampilannya, https://github.com/rust-lang/book/pull/134

Dengan try !, jelas bahwa mungkin ada pengembalian di suatu tempat, karena makro dapat melakukannya.
Ini hanya jelas karena Anda terbiasa dengan cara kerja makro di Rust. Mana yang akan sama persis sekali? stabil, tersebar luas, dan dijelaskan di setiap pengantar Rust.

Dalam bahasa apa pun yang saya ketahui "makro" berarti "di sini ada naga" dan bahwa apa pun bisa terjadi. Jadi saya akan mengubah kalimat itu menjadi "karena Anda sudah terbiasa dengan cara kerja makro", tanpa bagian "di Rust".

@hauleth

biarkan a = coba! (x? .y? .z);

Saya telah mendalilkan ini. Saya pikir ini akan menjadi solusi yang tepat.

Saya sangat tidak setuju. Karena Anda akan mendapatkan simbol ajaib yang hanya berfungsi di try! dan tidak di luar.

@hauleth

biarkan a = coba! (x? .y? .z);
Saya telah mendalilkan ini. Saya pikir ini akan menjadi solusi yang tepat.
Saya sangat tidak setuju. Karena Anda akan mendapatkan simbol ajaib yang hanya berfungsi saat mencoba! dan bukan di luar.

Saya belum mengatakan bahwa ? seharusnya hanya berfungsi di try! . Apa yang saya katakan adalah bahwa ? harus bekerja seperti operator pipa yang akan mendorong data ke aliran dan mengembalikan kesalahan segera setelah itu terjadi. try! tidak diperlukan dalam kasus itu, tetapi dapat digunakan dalam konteks yang sama seperti yang digunakan sekarang.

@steveklabnik Saya menganggap Rust sebagai bahasa yang memiliki pengembalian implisit, Dalam contoh Anda, 5 tidak dikembalikan secara implisit, tetapi mari kita ambil ini:

fn test() -> i32 {
    5
}

Di sini, 5 dikembalikan secara implisit, bukan? Berbeda dengan return 5; Anda perlukan dalam contoh Anda. Ini menghasilkan 2 cara berbeda untuk mengembalikan nilai. Yang menurut saya agak membingungkan tentang Rust. Menambahkan sepertiga tidak akan membantu IMO.

Bukan itu. Ini adalah hasil dari ekspresi, khususnya, function body. "implicit return" menyiratkan bahwa Anda entah bagaimana bisa kembali secara implisit dari mana saja, tapi itu tidak benar. Tidak ada bahasa berbasis ekspresi lain yang menyebut ini sebagai "pengembalian implisit", karena itu akan menjadi contoh kode saya di atas.

@steveklabnik Baiklah, terima kasih telah meluangkan waktu untuk menjelaskan ini: +1:

Semuanya baik! Saya benar-benar dapat melihat dari mana Anda berasal, itu hanya dua hal berbeda yang sering digunakan orang dengan cara yang salah. Saya telah melihat orang-orang berasumsi "pengembalian implisit" berarti Anda dapat meninggalkan ; di mana saja di sumber untuk mengembalikan .... bahwa _would_ menjadi sangat buruk: smile:

@hauleth The? operator hanya akan menjadi gula sintaksis untuk and_then dalam kasus itu. Dengan cara itu Anda dapat menggunakannya di lebih banyak kasus dan tidak akan sulit untuk melewatkan pengembalian. Ini juga apa yang semua bahasa lain lakukan yang memiliki? operator. Rust's? operator dalam implementasi saat ini akan menjadi lawan yang tepat dari apa yang semua bahasa lain lakukan. Juga and_then adalah pendekatan fungsional dan tetap dianjurkan, karena memiliki aliran kontrol yang jelas. Jadi hanya bikin? gula sintaksis untuk and_then dan kemudian pertahankan percobaan saat ini! untuk secara eksplisit "membuka dan mengembalikan", tampaknya menjadi situasi yang jauh lebih bersih, dengan membuat pengembalian lebih terlihat dan? operator lebih fleksibel (dengan dapat menggunakannya dalam kasus non-return seperti pencocokan pola).

Persis.

Łukasz Niemier
[email protected]

Wiadomość napisana przez Christopher Serr [email protected] w dniu 02.09.2016, astaga. 21:05:

@hauleth https://github.com/hauleth The? operator hanya akan menjadi gula sintaksis untuk and_then dalam kasus itu. Dengan cara itu Anda dapat menggunakannya di lebih banyak kasus dan tidak akan sulit untuk melewatkan pengembalian. Ini juga apa yang semua bahasa lain lakukan yang memiliki? operator. Rust's? operator dalam implementasi saat ini akan menjadi lawan yang tepat dari apa yang semua bahasa lain lakukan. Juga and_then adalah pendekatan fungsional dan tetap dianjurkan, karena memiliki aliran kontrol yang jelas. Jadi hanya bikin? gula sintaksis untuk and_then dan kemudian pertahankan percobaan saat ini! untuk secara eksplisit "membuka dan mengembalikan", tampaknya menjadi situasi yang jauh lebih bersih, dengan membuat pengembalian lebih terlihat dan? operator lebih fleksibel (dengan dapat menggunakannya dalam kasus non-return seperti pencocokan pola).

-
Anda menerima ini karena Anda disebutkan.
Balas email ini secara langsung, lihat di GitHub https://github.com/rust-lang/rust/issues/31436#issuecomment -244461722, atau nonaktifkan utas https://github.com/notifications/unsubscribe-auth/ AARzN5-w4EO9_FwNMDpvtYkGUuQKGt-Kks5qmHOHgaJpZM4HUm_-.

Dan ketika mengerjakan Permintaan Tarik untuk repo Rust saya sebenarnya harus bekerja dengan kode yang menggunakan? operator dan pada kenyataannya itu sangat merugikan keterbacaan bagi saya, seperti halnya; sangat tersembunyi (secara mental, karena hanya suara yang disaring di otak) dan saya sering mengabaikannya. Dan menurut saya itu cukup menakutkan.

@steveklabnik kita menyebutnya "kembali implisit", karena kita tidak satu -satunya orang .

@hauleth ya, selama bertahun-tahun saya di Ruby, saya belum pernah mendengar ada orang yang menyebutnya pengembalian implisit. Saya masih berpendapat bahwa itu adalah cara berpikir yang salah.

Saya telah menggunakan ? dalam beberapa proyek dan lebih suka try! , terutama karena posisinya di postfix. Secara umum, kode Rust "asli" cukup banyak memasukkan Result 'monad' di main dan tidak pernah meninggalkannya kecuali pada node daun sesekali; dan kode seperti itu diharapkan selalu menyebarkan kesalahan. Untuk sebagian besar, tidak masalah ekspresi mana yang menghasilkan kesalahan - mereka semua hanya dikirim kembali ke tumpukan, dan saya tidak ingin melihatnya ketika saya membaca logika utama kode.

Perhatian utama saya dengan ? adalah saya bisa mendapatkan manfaat yang sama - posisi postfix - dengan makro metode, jika ada. Saya memiliki kekhawatiran lain bahwa mungkin dengan menstabilkan formulasi saat ini kami membatasi ekspresifitas masa depan dalam penanganan kesalahan - konversi Result ini tidak cukup untuk membuat penanganan kesalahan di Rust seergonomis yang saya inginkan; kami telah membuat beberapa kesalahan desain dengan penanganan kesalahan Rust yang tampaknya sulit diperbaiki, dan ini mungkin menggali lebih dalam; meskipun saya tidak memiliki bukti konkret.

Saya menulis ini berkali-kali sebelumnya tetapi saya benar-benar jatuh cinta dengan ? dan kemungkinan sifat pembawa. Saya mengonversi satu proyek ke menggunakan ? seluruhnya dan itu membuat banyak hal menjadi mungkin (terutama yang berkaitan dengan rantai) yang terlalu rumit dengan try! . Saya juga untuk bersenang-senang membahas beberapa proyek lain untuk melihat bagaimana mereka akan melakukannya dengan ? dan secara keseluruhan saya tidak menemui masalah dengan itu.

Karena itu saya memberikan +1 yang sangat besar untuk menstabilkan ? berdasarkan sifat yang lebih baik bernama Carrier yang idealnya juga mencakup beberapa kasus lain yang saya bahas dalam diskusi lain tentang itu.

Perhatian utama saya dengan? adalah saya bisa mendapatkan manfaat yang sama - posisi postfix - dengan makro metode, jika ada.

Mungkin kita membutuhkan RFC untuk ini? Kebanyakan orang sepertinya menyukai fungsi dari?, Tetapi bukan? diri.

Mungkin kita membutuhkan RFC untuk ini? Kebanyakan orang sepertinya menyukai fungsi dari?, Tetapi bukan? diri.

Ada RFC dengan banyak diskusi untuk ini. Juga, saya tidak tahu dari mana Anda mendapatkan "kebanyakan orang" itu. Jika berasal dari peserta dalam masalah ini, tentu Anda akan melihat lebih banyak orang membantah karena stabilisasi _sudah_ menjadi tindakan default tim.
? telah dibahas secara mendalam sebelum RFC digabungkan, dan sebagai pendukung, agak melelahkan harus melakukan hal yang sama ketika membahas stabilisasi.

Bagaimanapun, saya akan memasukkan +1 saya untuk sentimen @mitsuhiko di sini.

Ada RFC dengan banyak diskusi tentang ini. Juga, saya tidak tahu dari mana Anda mendapatkan "kebanyakan orang" itu. Jika berasal dari peserta dalam masalah ini, tentu Anda akan melihat lebih banyak orang berdebat karena stabilisasi sudah menjadi tindakan default tim.

Maaf, komentar saya terlalu singkat. Saya mengacu pada membuat RFC untuk beberapa jenis "makro metode", misalnya func1().try!().func2().try!() (Sejauh yang saya tahu, ini saat ini tidak memungkinkan).

Secara pribadi saya suka? operator, tetapi saya memiliki kekhawatiran yang sama dengan @brson , dan saya pikir akan lebih baik untuk menjelajahi alternatif sebelum kami menstabilkan fitur ini. Termasuk konversi RFC, utas ini, dan utas internal yang ditautkan oleh @nikomatsakis , pasti masih ada beberapa perselisihan tentang fitur ini, meskipun argumennya sama berulang kali. Namun jika tidak ada alternatif yang layak, menstabilkan paling masuk akal.

Tampaknya terlalu dini untuk menstabilkan fitur tanpa menerapkannya sepenuhnya - dalam hal ini, ekspresi catch {..}.

Saya telah menyatakan kekhawatiran saya atas fitur ini sebelumnya, dan saya masih yakin itu ide yang buruk. Saya pikir memiliki operator pengembalian bersyarat postfix tidak seperti apa pun dalam bahasa pemrograman lain, dan mendorong Rust melewati anggaran kompleksitasnya yang sudah terbentang.

@mcpherrinm Bahasa lain malah menyembunyikan jalur operator() an "operator pengembalian bersyarat"?

Adapun anggaran kompleksitas, itu hanya secara sintaksis berbeda dari try! , setidaknya bagian yang Anda keluhkan.
Apakah argumen melawan try! -kode berat, yang hanya membuat ? lebih mudah dibaca?
Jika demikian, maka saya setuju jika ada alternatif serius selain "tidak memiliki otomatisasi propagasi kesalahan _at all_".

Menyarankan kompromi: https://github.com/rust-lang/rfcs/pull/1737

Mungkin tidak ada kesempatan untuk diterima, tapi saya tetap berusaha.

Saya suka ide @keeperofdakeys tentang "makro metode". Saya tidak berpikir sintaks ? harus diterima karena alasan yang sama operator terner tidak berkarat - keterbacaan. ? itu sendiri tidak mengatakan apa-apa. Sebaliknya, saya lebih suka melihat kemampuan untuk menggeneralisasi perilaku ? dengan "metode makro".

a.some_macro!(b);
// could be syntax sugar for
some_macro!(a, b); 
a.try!();
// could be syntax sugar for
try!(a); 

Dengan cara ini, akan jelas apa perilakunya, dan memungkinkan untuk dirantai dengan mudah.

Makro metode seperti result.try!() tampaknya merupakan peningkatan yang lebih umum untuk ergonomi bahasa dan terasa kurang ad-hoc daripada operator ? .

@tokopedia

Saya memiliki kekhawatiran lain bahwa mungkin dengan menstabilkan formulasi saat ini kami membatasi ekspresifitas masa depan dalam penanganan kesalahan - konversi Hasil saat ini tidak cukup untuk membuat penanganan kesalahan di Rust seergonomis yang saya inginkan

Ini adalah hal yang menarik. Ada baiknya menghabiskan waktu terfokus pada hal ini (mungkin Anda dan saya bisa mengobrol sedikit). Saya setuju kita bisa lebih baik di sini. Desain yang diusulkan untuk ciri Carrier (lihat https://github.com/rust-lang/rfcs/issues/1718) dapat membantu di sini, terutama jika dikombinasikan dengan spesialisasi, karena membuat segalanya lebih fleksibel.

Saya benar-benar ragu bahwa makro metode akan menjadi ekstensi yang baik untuk bahasa tersebut.

macro_rules! makro saat ini dideklarasikan dengan cara yang mirip dengan fungsi bebas, dan akan menjadi lebih analog ketika sistem impor baru diadopsi untuk mereka. Yang saya maksud adalah mereka dideklarasikan seperti item tingkat atas dan dipanggil seperti item tingkat atas, dan segera mereka juga akan diimpor seperti item tingkat atas.

Ini bukanlah cara kerja metode. Metode memiliki properti berikut:

  1. Tak dapat dideklarasikan dalam lingkup modul, tapi harus dideklarasikan dalam blok impl .
  2. Diimpor dengan tipe / sifat yang diasosiasikan dengan blok impl , daripada diimpor secara langsung.
  3. Dikirim berdasarkan jenis penerima mereka, bukan dikirim berdasarkan simbol tunggal yang tidak ambigu dalam cakupan ini.

Karena makro diperluas sebelum pemeriksaan ketik, tidak satu pun dari properti ini yang bisa benar dari makro menggunakan sintaks metode sejauh yang saya tahu. Tentu saja kita bisa saja memiliki makro yang menggunakan sintaks metode tetapi dikirim dan diimpor dengan cara yang sama seperti makro 'gratis', tetapi menurut saya perbedaannya akan menjadikannya fitur yang sangat membingungkan.

Untuk alasan ini saya pikir itu bukan pilihan yang baik untuk menunda ? dengan keyakinan bahwa "metode makro" mungkin suatu hari muncul.

Selain itu, menurut saya ada garis di mana beberapa konstruk digunakan secara luas dan penting sehingga harus dipromosikan dari makro ke gula. for loop adalah contoh yang bagus. Perilaku ? merupakan bagian integral dari kisah penanganan kesalahan Rust, dan saya pikir itu tepat untuk menjadi gula kelas satu daripada makro.

@hauleth , @Cantik

Untuk menanggapi mereka yang menyarankan bahwa ? harus menjadi operator and_then , ini berfungsi dengan baik dalam bahasa seperti Kotlin (saya tidak terbiasa dengan coffeescript) karena penggunaan ekstensif fungsi ekstensi tetapi tidak begitu mudah dalam berkarat. Pada dasarnya, sebagian besar penggunaan and_then bukanlah maybe_i.and_then(|i| i.foo()) , melainkan maybe_i.and_then(|i| Foo::foo(i)) Yang pertama dapat dinyatakan sebagai maybe_i?.foo() tetapi yang terakhir tidak bisa. Seseorang dapat mengatakan bahwa Foo::foo(maybe_i?, maybe_j?) berubah menjadi maybe_i.and_then(|i| maybe_j.and_then(|j| Foo::foo(i, j))) tetapi ini terasa lebih membingungkan daripada hanya mengatakan bahwa karat kembali awal saat mencapai ? yang mengevaluasi kesalahan. Namun, ini bisa dibilang lebih ampuh.

@Stebalien Dalam RFC yang diterima, catch { Foo::foo(maybe_i?, maybe_j?) } melakukan apa yang Anda inginkan.

@eddyb Poin yang bagus. Saya kira saya bisa meninggalkan "Namun, ini bisa dibilang akan lebih kuat". Itu bermuara pada tangkapan implisit / percobaan eksplisit versus tangkapan eksplisit / percobaan implisit:

let x: i32 = try Foo::foo(a?.b?.c()?));
let y: Result<i32, _> = Foo::foo(a?.b?.c()?);

Melawan:

let x: i32 = Foo::foo(a?.b?.c()?);
let y: Result<i32, _> = catch  Foo::foo(a?.b?.c()?);

(sintaks modulo)

@Stebalien Contoh lain: jika saya ingin meneruskan Foo ke fungsi bar , dengan proposal Anda, saya perlu:

bar(Foo::foo(a?.b?.c()?)?)

Apakah ini yang ada dalam pikiran Anda? Perhatikan tambahan ? , tanpa itu bar akan mendapatkan Result bukan Foo .

@eddyb Mungkin. Catatan: Saya sebenarnya tidak mengusulkan ini! Saya berpendapat bahwa menggunakan ? sebagai operator pipa tidak terlalu berguna dalam karat tanpa beberapa cara untuk menangani kasus Foo::foo(bar?) .

Hanya untuk dicatat bahwa saya benci gagasan makro metode dan saya tidak dapat memikirkan fitur bahasa yang akan saya lawan lebih kuat. Mereka mengaburkan pentahapan kompiler, dan kecuali kita membuat perubahan yang sangat mendasar pada bahasa, tidak mungkin mereka ada dan memiliki perilaku yang tidak mengejutkan. Mereka juga sulit diurai secara masuk akal dan hampir pasti tidak kompatibel ke belakang.

@Stebalien , dengan ? sebagai operator pipa Foo::foo(bar?) akan terlihat seperti ini: Foo::foo(try!(bar)) dan bar(Foo::foo(a?.b?.c()?)?) (dengan asumsi Foo::foo : fn(Result<_, _>) -> Result<_, _> ): bar(try!(Foo::foo(a?.b?.c()?))) .

@ hauleth maksud saya adalah bahwa Foo::foo(bar?)? _much_ lebih umum daripada bar?.foo()? berkarat. Oleh karena itu, agar berguna, ? harus mendukung kasus ini (atau beberapa fitur lain harus diperkenalkan). Saya mendalilkan cara untuk melakukannya dan menunjukkan bahwa cara itu setidaknya akan berantakan. Inti dari ? adalah untuk menghindari penulisan try!(foo(try!(bar(try!(baz()))))) (2x tanda kurung!); biasanya tidak mungkin untuk menulis ulang ini sebagai try!(baz()?.bar()?.foo()) .

Tetapi Anda selalu dapat melakukan:

try!(baz().and_then(bar).and_then(foo))

Łukasz Niemier
[email protected]

Wiadomość napisana przez Steven Allen [email protected] w dniu 05.09.2016, ya Tuhan. 15:39:

@hauleth https://github.com/hauleth maksud saya adalah Foo :: foo (bar?)? jauh lebih umum daripada bar? .foo ()? berkarat. Karena itu, agar bermanfaat,? harus mendukung kasus ini (atau beberapa fitur lain harus diperkenalkan). Saya mendalilkan cara untuk melakukannya dan menunjukkan bahwa cara itu setidaknya akan berantakan. Inti dari? adalah untuk dapat menghindari penulisan try! (foo (try! (bar (try! (baz ()))))) (2x tanda kurung!); biasanya tidak mungkin untuk menulis ulang ini sebagai try! (baz () ?. bar () ?. foo ()).

-
Anda menerima ini karena Anda disebutkan.
Balas email ini secara langsung, lihat di GitHub https://github.com/rust-lang/rust/issues/31436#issuecomment -244749275, atau nonaktifkan utas https://github.com/notifications/unsubscribe-auth/ AARzN1Hdk6uk5-SoYawtgAbJUDf_8MsMks5qnBumgaJpZM4HUm_-.

Pada catatan yang sedikit terkait, tampaknya? -Fitur terutama digunakan oleh pembangun, jadi kita mungkin dapat menghindari kebutuhan akan -fitur dengan menyediakan cara mudah untuk membangun pembangun yang membungkus Hasil. Saya mengusulkan sesuatu di sini, tetapi mungkin perlu lebih banyak pekerjaan.

https://github.com/colin-kiegel/rust-derive-builder/issues/25

Terima kasih atas pemikiran Anda tentang ide makro metode @nrc dan @withoutboats , ada baiknya mendengar beberapa alasan konkret mengapa mereka tidak berhasil.

@nielsle Saya rasa tidak tepat untuk mengatakan bahwa ? "terutama" digunakan oleh pembangun. Sementara pembangun adalah contoh di mana menurut saya keuntungan dari operator postfix yang ringan benar-benar bersinar, saya lebih suka ? hingga try! dalam setiap konteks.

@nielsle tentang masa depan, saya awalnya prihatin karena alasan yang sama. Tapi, setelah memikirkannya, saya pikir async / await akan menggantikan kebutuhan ? dalam konteks itu. Mereka sebenarnya cukup ortogonal: Anda dapat melakukan hal-hal seperti (await future)?.bar() .

(Mungkin akan lebih baik memiliki operator sufiks daripada kata kunci await sehingga tanda kurung tidak diperlukan. Atau mungkin preseden yang disetel dengan cermat sudah cukup.)

Saya pasti ingin melihat beberapa dokumentasi ditulis sebelum kami menstabilkan. Saya melihat referensi dan tidak dapat menemukan sebutan apapun. Di mana kami harus mendokumentasikan fitur ini?

@cbreeden Saya tahu @steveklabnik umumnya menghindari pendokumentasian fitur yang tidak stabil, karena ada kemungkinan akan membuang-buang waktu jika fitur tersebut tidak pernah stabil. Saya tidak tahu apakah kami pernah memblokir stabilisasi pada penulisan dokumentasi sebelumnya.

@solson Anda benar, ini mungkin bukan tempat untuk mengemukakannya - atau setidaknya tidak terkait dengan pertanyaan stabilisasi. Saya kira saya hanya membayangkan situasi di mana kami dapat memutuskan untuk menstabilkan fitur, tetapi kemudian juga memerlukan dokumentasi sebelum dirilis ke rustc stabil. Ada RFC yang terkait dengan mengintegrasikan dokumentasi dengan stabilisasi dan rilis fitur, jadi saya hanya akan menunggu proses itu stabil (tetapi bukan tanpa dokumentasi yang tepat terlebih dahulu, tentu saja)

Saya pikir bagian penting dari RFC ini adalah memiliki sesuatu di sisi kanan ekspresi yang bertindak seperti try! , karena itu membuat penggunaan "try" _much_ lebih terbaca dan memiliki "catch". . Awalnya saya adalah 100% pendukung untuk menggunakan ? sebagai sintaks tetapi baru-baru ini saya tersandung tentang beberapa kode (bersih!) Sudah menggunakan ? yang membuat saya sadar bahwa di luar dari contoh sederhana ? sangat mudah diabaikan . Yang sekarang membuat saya percaya bahwa menggunakan ? sebagai sintaks untuk "percobaan baru" mungkin merupakan kesalahan besar.

Oleh karena itu, saya mengusulkan bahwa mungkin ide yang baik untuk _menghasilkan beberapa polling_ sebelum menyelesaikannya (dengan pemberitahuan tentang hal itu di Forum) untuk mendapatkan umpan balik tentang penggunaan ? atau simbol lain sebagai sintaks . Secara optimal dengan contoh penggunaan dalam fungsi yang lebih lama. Perhatikan bahwa saya hanya mempertimbangkan bahwa mungkin ide yang baik untuk mengganti nama ? untuk tidak mengubah apa pun. Jajak pendapat dapat mencantumkan nama lain yang mungkin muncul di masa lalu seperti ?! (atau ?? ) atau hanya sesuatu seperti "gunakan? Vs. gunakan lebih dari pada karakter".

Btw. tidak menggunakan ? mungkin juga memuaskan orang yang tidak menyukainya karena sintaksnya sama dengan jenis opsional bahasa lain. Dan mereka yang ingin menjadikannya sintaks opsional untuk jenis Opsi karat. (Melalui ini tidak ada masalah yang saya bagikan).

Selain itu, saya pikir dengan jajak pendapat seperti itu, orang yang biasanya tidak mengambil bagian dalam proses RFC dapat dihubungi. Biasanya ini mungkin tidak diperlukan tetapi try! => ? adalah perubahan yang sangat besar bagi siapa pun yang menulis dan / atau membaca kode karat.

PS:
Saya memberikan inti dengan fungsi seperti di atas dalam variasi yang berbeda ("?", "?!", "??") melalui Saya tidak tahu apakah masih ada lagi. Juga ada RFC untuk mengganti nama ? menjadi ?! yang diarahkan ke diskusi ini.

Maaf, tentang kemungkinan memulai kembali diskusi yang sudah berlangsung lama: smiley_cat:.

(Perhatikan bahwa ?? buruk jika Anda masih ingin memperkenalkan ? untuk Opsi karena expr??? akan ambigu)

? sangat mudah diabaikan

Apa yang kita bahas di sini? Kode sama sekali tidak disorot? Penjelajahan kode yang disorot secara teratur?
Atau secara aktif mencari ? dalam suatu fungsi?

Jika saya memilih satu ? di editor saya, _all_ yang lain ? dalam file disorot dengan latar belakang kuning cerah, dan fungsi pencarian juga berfungsi, jadi saya tidak melihat yang terakhir itu sebagai berpose _any_ kesulitan bagi saya.

Sedangkan untuk kasus lain, saya lebih suka menyelesaikan ini dengan penyorotan yang lebih baik daripada berakhir dengan ?? atau ?! .

@dathinab saya pikir

let namespace = namespace_opt.ok_or(Error::NoEntry).try!();

Dengan cara itu sulit untuk dilewatkan, tetapi sama mudahnya, bahkan lebih mudah untuk mengetik daripada .unwrap() .

@eddyb : ini tentang kode yang disorot normal, misalnya di github. Misalnya saat membaca melalui basis kode. Dari sudut pandang saya, rasanya agak salah jika saya perlu penyorotan yang kuat untuk tidak dengan mudah mengabaikan ? yang keduanya akan memperkenalkan jalur pengembalian lain (/ path to catch) dan kemungkinan mengubah jenis variabel dari Result<T> sampai T

@CryZe : Saya setuju dengan Anda tetapi saya tidak berpikir kita bisa mendapatkan ini dalam waktu dekat. Dan memiliki sesuatu yang sedikit lebih pendek dari .try!() juga tidak terlalu buruk.

@CryZe Saya suka sintaks itu juga, tetapi @withoutboats menyebutkan beberapa alasan kuat mengapa metode makro dapat merusak bahasa.
Di sisi lain, saya khawatir jika makro metode benar-benar muncul, menurut saya mereka tidak akan berfungsi dengan baik dengan ? .

Saya tidak secara inheren menentang penggunaan dua karakter untuk sigil ini, tetapi saya melihat contoh & saya tidak menemukan perubahan yang membuat ? tampak lebih terlihat oleh saya.

Ya sama. Saya pikir ini perlu semacam kata kunci, karena simbol secara umum lebih banyak digunakan untuk penataan, baik dalam bahasa normal maupun bahasa pemrograman.

@CryZe : Mungkin sesuatu seperti ?try akan menjadi semacam kata kunci. Tapi sejujurnya saya tidak suka ( ?try ).

@deddyb

dan fungsi pencarian juga berfungsi

Menelusuri ? akan menghasilkan positif palsu, sedangkan versi yang lebih lama kemungkinan besar tidak.

Mencari ? akan menampilkan positif palsu, sementara versi yang lebih lama kemungkinan besar tidak.

Saya berharap penyorot sintaks akan menangani ini secara internal (menghindari positif palsu). Faktanya, mereka bahkan dapat memasukkan beberapa bentuk penanda "mungkin kembali" di pinggiran (di sebelah nomor baris).

Sebagai contoh,
screen-2016-09-15-175131

Wow, itu pasti sangat membantu. Tapi kemudian Anda benar-benar perlu memastikan Anda
memiliki editor Anda dikonfigurasi dengan benar, yang tidak dapat Anda lakukan sepanjang waktu (seperti
di GitHub misalnya).

15-09-2016 23:52 GMT + 02.00 Steven Allen [email protected] :

Sebagai contoh,
[image: screen-2016-09-15-175131]
https://cloud.githubusercontent.com/assets/310393/18568833/1deed796-7b6d-11e6-99af-75f0d7ddd778.png

-
Anda menerima ini karena Anda disebutkan.
Balas email ini secara langsung, lihat di GitHub
https://github.com/rust-lang/rust/issues/31436#issuecomment -247465972,
atau nonaktifkan utasnya
https://github.com/notifications/unsubscribe-auth/ABYmbjyrt07NXKMUdmlBfaciRZq7uBVEks5qqb4sgaJpZM4HUm_-
.

@CryZe Saya cukup yakin GitHub hanya menggunakan penyorot sintaks sumber terbuka; kita bisa menambalnya :-).

Saya pikir secara umum adalah ide yang baik untuk proyek Rust untuk membuat rekomendasi bahwa penyorot sintaks untuk Rust harus menggunakan gaya yang sangat terlihat pada ? , untuk menyeimbangkan kekhawatiran tentang visibilitas.

Saya pikir ? adalah pilihan terbaik yang dapat kami buat di sini, dan kami telah menggunakannya cukup banyak di Miri .

Mendengar kembali ke motivasi asli, try!(...) menonjol dan membuatnya sulit untuk mengabaikan aliran kesalahan dan hanya membaca jalur bahagia. Ini adalah sisi negatifnya dibandingkan dengan pengecualian tak terlihat dari bahasa tradisional. Memperluas ? ke kata kunci yang lebih terlibat akan memiliki kerugian yang sama.

Di sisi lain, dengan ? , ketika saya tidak peduli tentang aliran kesalahan, saya bisa mengabaikannya dan membiarkannya memudar ke latar belakang. Dan ketika saya benar-benar peduli dengan aliran kesalahan, saya masih bisa melihat ? baik-baik saja. Menyoroti ? terang-terangan bahkan tidak perlu bagi saya, tetapi jika itu membantu orang lain, itu bagus. Ini adalah peningkatan dari pengecualian tak terlihat dan try! .

Beralih ke sigil yang lebih besar sepele seperti ?! tidak akan membantu saya dengan cara apa pun, tetapi membuat pembacaan dan penulisan kode penanganan kesalahan sedikit lebih buruk.

Terima kasih semuanya untuk periode komentar terakhir yang hangat (serta utas internal sebelumnya ). Mengingat bahwa kita berbicara tentang RFC yang paling banyak dikomentari hingga saat ini , saya tidak terkejut melihat bahwa diskusi seputar stabilisasi juga cukup aktif.

Ijinkan saya langsung mengejar: Tim @ rust-lang / lang telah memutuskan untuk menstabilkan operator ? ketika diterapkan ke nilai tipe Result . Perhatikan bahwa fitur catch belum distabilkan (dan memang belum diimplementasikan); demikian pula, yang disebut "sifat pembawa", yang berarti memperluas ? ke tipe seperti Option , masih dalam fase diskusi pra-RFC . Namun kami telah mengambil langkah-langkah dalam implementasi saat ini untuk memastikan bahwa kami dapat menambahkan sifat Carrier nanti (yang membahas beberapa kekhawatiran saya sebelumnya tentang potensi interaksi dengan inferensi).

Saya ingin mengambil sedikit waktu untuk meringkas diskusi yang telah terjadi sejak FCP dimulai pada 22 Agustus . Banyak dari tema ini juga terjadi di utas RFC asli . Jika Anda tertarik untuk membaca utas, komentar FCP dan komentar rekapitulasi di utas tersebut mencoba untuk membahas percakapan secara mendalam. Dalam beberapa kasus, saya akan menautkan ke komentar di utas asli itu jika komentar itu lebih mendalam daripada yang sesuai dari utas ini.

Lingkup operator ? seharusnya ekspresi saat ini, bukan fungsi saat ini.

Ketika kesalahan terjadi, makro try! menyebarkan kesalahan itu tanpa syarat ke fungsi pemanggil (dengan kata lain, ia mengeksekusi return dengan kesalahan). Seperti yang dirancang saat ini, operator ? mengikuti preseden ini, tetapi dengan maksud untuk mendukung kata kunci catch yang memungkinkan pengguna menentukan cakupan yang lebih terbatas. Ini berarti, misalnya, x.and_then(|b| foo(b)) dapat ditulis sebagai catch { foo(x?) } . Sebaliknya, beberapa bahasa baru-baru ini menggunakan operator ? untuk mengartikan sesuatu yang lebih mirip dengan and_then , dan ada kekhawatiran bahwa hal ini akan membingungkan pengguna baru.

Akhirnya, setelah catch diimplementasikan, ini adalah pertanyaan default . Dan ada beberapa alasan mengapa kami yakin bahwa default "break out of function" (dengan opsi untuk menyesuaikan) lebih sesuai untuk ? di Rust :

  • makro try! telah membuktikan dirinya berkali-kali sebagai default yang sangat berguna . ? dimaksudkan sebagai pengganti try! , jadi wajar jika berperilaku sama.
  • ? di Rust terutama digunakan untuk menyebarkan hasil , yang lebih mirip dengan pengecualian. Semua sistem pengecualian default untuk menyebarkan kesalahan keluar dari fungsi saat ini dan ke pemanggil (bahkan yang, seperti Swift, juga memerlukan kata kunci untuk melakukannya).

    • sebaliknya, ? di Swift, Groovy, dan seterusnya berkaitan dengan nilai null atau tipe opsi.

    • jika kita mengadopsi sifat pembawa, operator ? di Rust masih dapat digunakan dalam skenario tersebut (dan khususnya dalam hubungannya dengan catch ), tetapi ini bukan kasus penggunaan _main_.

  • banyak penggunaan and_then di Rust hari ini tidak akan bekerja menggunakan notasi metode ; beberapa penggunaan masa depan yang diperkirakan (seperti di masa depan) dapat dimasukkan oleh transformasi async-await dalam hal apa pun

? mengaburkan aliran kontrol karena sulit dikenali.

Perhatian umum adalah bahwa operator ? terlalu mudah untuk diabaikan . Ini jelas merupakan tindakan penyeimbangan. Memiliki operator yang ringan memudahkan untuk fokus pada "jalur bahagia" saat Anda menginginkannya - tetapi penting untuk memiliki beberapa indikasi di mana kesalahan dapat terjadi (berbeda dengan pengecualian, yang memperkenalkan aliran kontrol implisit). Selain itu, mudah untuk membuat ? lebih mudah dikenali melalui penyorotan sintaks (mis., 1 , 2 ).

Mengapa tidak metode makro?

Salah satu keuntungan besar dari ? adalah dapat digunakan dalam posisi pasca-perbaikan, tetapi
kita bisa mendapatkan keuntungan serupa dari "makro metode" seperti foo.try! . Meskipun benar, makro metode membuka banyak kerumitan itu sendiri , terutama jika Anda ingin mereka berperilaku seperti metode (misalnya, dikirim berdasarkan jenis penerima, dan tidak menggunakan cakupan leksikal). Selain itu, menggunakan metode makro seperti foo.try! memiliki kesan bobot yang jauh lebih berat daripada foo? (lihat poin sebelumnya).

Kontrak apa yang harus ditawarkan From ?

Dalam diskusi RFC asli, kami memutuskan untuk menunda pertanyaan [apakah harus ada "kontrak" untuk From ] ((https://github.com/rust-lang/rust/issues/31436#issuecomment -180558025). Konsensus umum dari tim lang adalah bahwa seseorang harus melihat operator ? sebagai memanggil sifat From , dan bahwa sifat From secara alami dapat melakukan apapun diizinkan oleh tanda tangan tipe mereka. Perhatikan bahwa peran dari sifat From cukup terbatas di sini: ia hanya digunakan untuk mengonversi dari satu jenis kesalahan ke kesalahan lainnya (tetapi selalu dalam konteks Result ).

Namun , saya ingin mencatat bahwa diskusi mengenai sifat "pembawa" sedang berlangsung , dan mengadopsi konvensi yang kuat lebih penting dalam skenario itu . Secara khusus, sifat pembawa dapat menentukan apa yang merupakan "sukses" dan "kegagalan" untuk suatu jenis, serta apakah satu jenis nilai (misalnya, Option ) dapat diubah menjadi yang lain (misalnya, a Result ). Banyak yang berpendapat bahwa kami tidak ingin mendukung interkonversi sembarang antara tipe "seperti kesalahan" (mis., ? seharusnya tidak dapat mengubah Option menjadi Result ). Jelas, karena pengguna akhir dapat menerapkan sifat Carrier untuk tipe mereka sendiri sesuai pilihan mereka, ini pada akhirnya adalah pedoman, tapi saya pikir ini penting.

coba pelabuhan! menggunakan ?

Saya tidak berpikir kita bisa melakukan ini secara kompatibel dan kita harus membiarkan implementasi try! saja. Haruskah kita menghentikan try! ?

@nrc Mengapa tidak kompatibel dengan

@withoutboats try!(x) adalah (x : Result<_, _>)? dan kita mungkin bisa menerapkannya seperti itu jika kita _wanted_ to, tapi secara umum x? bisa menyimpulkan apapun yang mendukung Carrier ciri (di masa mendatang), salah satu contohnya adalah iter.collect()? yang dengan try! hanya akan menjadi Result tetapi secara realistis bisa menjadi Option .

Itu masuk akal. Saya pikir kami menerima bahwa menambahkan impls ke std dapat menyebabkan ambiguitas inferensi; mengapa tidak menerimanya di sini juga?

Bagaimanapun juga, menurut saya try! seharusnya sudah tidak digunakan lagi.

? lebih berguna dalam pola pembangun seperti konteks, sementara percobaan lebih berguna dalam metode bertingkat seperti konteks. Saya pikir itu tidak boleh ditinggalkan, apapun artinya.

@ est31 Mereka melakukan hal yang persis sama sekarang kecuali untuk inferensi. Tidak yakin apa yang Anda maksud sebenarnya, tetapi ? biasanya benar-benar lebih bersih (sekali lagi inferensi modulo). Bisakah Anda memberi contoh?

@eddyb foo()?.bar()?.baz() lebih bagus dari try!(try!(foo()).bar()).baz() , dan try!(bar(try!(foo()))) lebih bagus dari bar(foo()?)?

Saya menemukan ? lebih dapat dibaca dalam kedua kasus. Ini mengurangi kekacauan tanda kurung yang tidak perlu.

Kapan ini akan mendarat di kandang?

@ofek ini untuk semuanya, yang belum lengkap, jadi sulit untuk mengatakannya. https://github.com/rust-lang/rust/pull/36995 menstabilkan sintaks ? , yang seharusnya stabil di 1,14.

Jangan lupa menambahkan? operator ke referensi sekarang setelah stabil: https://doc.rust-lang.org/nightly/reference.html#unary -operator-expression

Dan @bluss menunjukkan bahwa buku itu juga sudah usang: https://doc.rust-lang.org/nightly/book/syntax-index.html

@tokopedia

Saya menentang perpanjangan? ke Opsi.

Itu tidak harus digunakan untuk tujuan kesalahan. Sebagai contoh, jika saya menggunakan metode pembungkus untuk mengambil get dan memetakannya dengan beberapa fungsi, maka saya ingin menyebarkan kasus None atas.

? dianggap hanya karena kesalahan; notasi jangan yang lebih umum untuk propagasi umum seperti ini adalah non-tujuan.

Pada 29 Okt 2016, 11:08 -0400, ticki [email protected] , menulis:

@tomaka (https://tomaka.com/)

Saya menentang perpanjangan? ke Opsi.

Itu tidak harus digunakan untuk tujuan kesalahan. Misalnya, jika saya menggunakan metode pembungkus untuk mengambil get dan memetakannya dengan beberapa fungsi, maka saya ingin dapat menyebarkan kasus None ke atas.

-
Anda menerima ini karena Anda disebutkan.
Balas email ini secara langsung, lihat di GitHub (https://github.com/rust-lang/rust/issues/31436#issuecomment-257096575), atau nonaktifkan utas (https://github.com/notifications/unsubscribe -auth / AABsipGIpTF1-7enk-z_5JRYYtl46FLPks5q42DCgaJpZM4HUm_-).

Ini (= ? itu sendiri) sekarang menjadi fitur stabil! (Dari Rust 1.13)

Dua masalah dokumentasi:

  • [x] Perbarui bab Penanganan Kesalahan dalam buku # 37750
  • [x] Perbarui Carrier trait docs untuk situasi saat ini # 37751

Perhatikan bahwa baik catch maupun Carrier belum diimplementasikan dengan benar, hanya fitur ? .

Carrier ada, dan pesan kesalahan merujuk padanya saat menggunakan ? , jadi akan lebih baik jika masalah Operator telah diperbaiki daripada ditolak. Dokumennya juga perlu diperbarui, karena dokumen merujuk padanya untuk diterapkan pada Option. (Yang salah).

35946 harus menghapus penyebutan Carrier dari pesan kesalahan. Sepertinya setidaknya kita harus menghapus Option mention dari Carrier docs.

Saya menambahkan T-libs ke terbitan ini karena interaksi dengan sifat Carrier

Hai; di # 31954 upcast untuk jenis kesalahan dilakukan menggunakan From (dan serupa di kepala saat ini ), tetapi RFC 243 dengan jelas menyatakan Into harus digunakan untuk konversi.

Apakah ada alasan mengapa From digunakan sebagai gantinya? Saat mencoba melakukan upcast ke beberapa jenis kesalahan umum (mis. io::Error , atau sesuatu yang lain di peti eksternal) From tidak dapat diterapkan untuk jenis kesalahan lokal.

(FWIW sebagai penulis RFC 243 saya tidak berpikir keras tentang apakah From atau Into lebih disukai, dan mungkin atau mungkin tidak membuat pilihan yang tepat. pertanyaannya harus diputuskan berdasarkan manfaatnya (yang pada titik ini mungkin termasuk kompatibilitas ke belakang), bukan apa yang tertulis di RFC.)

Sayangnya akan ada kemunduran. Jika tidak ada (non-sepele) From<...> instance untuk jenis kesalahan yang diterapkan, kompilator dapat menyimpulkan jenis dalam kasus tertentu, di mana ia tidak dapat menyimpulkannya saat menggunakan Into (himpunan From instance dibatasi oleh peti saat ini, set lengkap Into instance tidak diketahui saat menyusun peti).

Lihat https://play.rust-lang.org/?gist=6d3ee9f93c8b40094a80d3481b12dd00 ("disederhanakan" dari masalah dunia nyata yang melibatkan fmt::Error di src / librustc / util / ppaux.rs # L81 )

Ada RFC baru yang akan menggantikan yang lama dalam hal menjelaskan ? : rust-lang / rfcs / pull / 1859

Saya -1 tentang memiliki catch blok sama sekali, ketika mereka sudah jauh lebih ringkas dengan penutupan.

Penutupan mengganggu pernyataan kontrol aliran. ( break , continue , return )

Sekarang https://github.com/rust-lang/rfcs/pull/1859 telah digabungkan, dan mengklaim bahwa kami menggunakan kembali masalah ini sebagai masalah pelacakannya, saya ingin mengusulkan agar kami mengevaluasi kembali catch bagian dari RFC 243.

Saya tidak mengusulkan agar kita segera menyingkirkannya, tetapi antusiasme untuk catch tidak pernah sebesar ? dan menurut saya perlu dipastikan bahwa hal itu masih masuk akal dalam penjelasan dari idiom yang muncul seharga ? , dan yang diharapkan muncul sehubungan dengan Try .

Saya masih ingin menangkap; sebelumnya hari ini saya harus mengubah beberapa kode dengan cara yang dapat dihindari jika tangkapan stabil.

Fitur tersebut sudah diimplementasikan setiap malam dengan sintaks do catch , bukan?

@withoutboats Ya, saat ini do catch , karena catch { ... } bentrok dengan struct literals ( struct catch { }; catch { } ).
.
@archshift Seperti yang ditunjukkan @SimonSapin di atas, penutupan mengganggu break , continue , dan return .

@bstrie Saya menemukan bahwa saya cukup sering menginginkan catch hari ini; itu muncul banyak selama refactoring.

Saya tidak menyadari bahwa kami berencana meminta do catch sebagai sintaks. Mengingat bahwa risiko kerusakan dunia nyata tampaknya sangat rendah (keduanya melanggar pedoman penamaan struct dan harus memiliki konstruktor menjadi ekspresi pertama dalam pernyataan (yang jarang terjadi di luar posisi pengembalian)), bisakah kita memanfaatkan rustfmt untuk tulis ulang pengenal yang menyinggung menjadi catch_ ? Jika itu membutuhkan Rust 2.0 untuk melakukannya, maka, saya selalu menjadi orang yang mengatakan bahwa Rust 2.0 seharusnya hanya berisi perubahan pemutusan yang sepele ...: P

@bstrie Kami benar-benar tidak ingin membutuhkan do catch jangka panjang. Diskusi yang mengarah pada penggunaan sintaks tersebut untuk saat ini ada di sini: https://github.com/rust-lang/rust/pull/39921

Luar biasa, terima kasih untuk konteksnya.

Saya baru saja datang ke sini karena saya berharap catch stabil, dan harus mempelajarinya tidak - jadi ya, tentu saja, sekarang ? stabil, akan sangat menyenangkan juga memiliki catch .

Saya ingin melihat seberapa jauh kami telah menyelesaikan masalah ini dan melihat diskusi do catch.

Saya telah mempertimbangkan ide yang mungkin konyol yang akan membantu dalam kasus seperti ini: izinkan secara opsional untuk memberi awalan kata kunci dengan @ atau beberapa sigil lainnya, kemudian buat semua kata kunci baru hanya menggunakan sigil. Kami juga mengalami masalah serupa dengan diskusi coroutine. Saya tidak ingat apakah saya membahas ini sebagai solusi di sana - saya mungkin punya - tetapi sepertinya ini mungkin terus muncul.

FWIW, C # sebenarnya mendukung yang sebaliknya: @ untuk menggunakan kata kunci sebagai pengenal. Itu biasa terlihat di Razor, di mana Anda meneruskan hal-hal seperti new { <strong i="6">@class</strong> = "errorbox" } untuk mengatur properti pada node HTML.

@tokopedia
Itu menarik. Saya tidak tahu tentang itu. Tetapi untuk Rust kita harus pergi ke arah lain karena kompatibilitas.

Ide bagus di pihak mereka. Ekosistem .net memiliki banyak bahasa, semua dengan kata kunci yang berbeda dan semuanya dapat saling memanggil.

Satu kemungkinan lain untuk masa depan kata kunci: letakkan di belakang atribut, seperti semacam stabil #[feature] .

Saya rasa masalah ini akan membutuhkan solusi yang lebih umum dalam jangka panjang.

@camlorn Utas ini mungkin menarik bagi Anda, khususnya ide Aaron tentang Rust "epochs": https://internals.rust-lang.org/t/pre-rfc-stable-features-for-breaking-changes/5002

Saya pikir kita harus menautkan https://github.com/rust-lang/rust/issues/42327 dari deskripsi masalah di sini. (Juga mungkin teks RFC harus diperbarui untuk ditautkan ke sana daripada di sini.)

(EDIT: Saya memposting komentar di sana, tidak yakin siapa yang sudah atau belum berlangganan!)

@camlorn Itu bisa, bagaimanapun, membuka "rustfmt melakukan penulisan ulang sepele untuk sementara waktu, kemudian itu menjadi jalan" kata kunci. AKA memanfaatkan "tidak rusak jika ada anotasi tambahan yang mungkin telah ditulis yang akan membuatnya bekerja di kedua" pintu keluar dalam jaminan stabilitas. Dan, mirip dengan UFCS untuk perubahan inferensi, model hipotetis "penyimpanan dalam bentuk yang dielaborasi sepenuhnya" dapat membuat peti tua tetap berfungsi.

Saya tidak akan keberatan jika sintaks untuk blok penangkap hanya do { … } atau bahkan ?{ … }

do memiliki properti bagus karena sudah menjadi kata kunci. Ini memiliki properti meragukan (tergantung pada perspektif Anda) untuk meminta notasi seperti do -seperti Haskell, meskipun itu tidak pernah menghentikan penggunaan sebelumnya, dan yang ini agak lebih dekat dalam kasus penggunaan.

itu juga terlihat seperti, tetapi berperilaku berbeda dari javascript yang diusulkan melakukan ekspresi

Proposal itu tidak terlalu relevan dengan Rust, yang sudah menggunakan blok kosong sebagai ekspresi.

Saya baru saja menunjukkan kemungkinan kebingungan karena keduanya akan terlihat sama tetapi melakukan hal yang sama sekali berbeda.

Ini memiliki properti meragukan (tergantung pada perspektif Anda) untuk meminta notasi seperti Haskell

@rpjohnst Hasil dan Opsi adalah monad, jadi setidaknya secara konseptual serupa. Ini juga harus kompatibel ke depan untuk memperpanjangnya di masa depan untuk mendukung semua monad.

Benar, di masa lalu keberatannya adalah jika kita menambahkan do untuk sesuatu yang secara konseptual mirip dengan monad, tetapi tanpa mendukung sepenuhnya, itu akan membuat orang sedih.

Pada saat yang sama, sementara kita mungkin bisa membuatnya kompatibel dengan notasi penuh, kita mungkin sebaiknya tidak menambahkan notasi penuh. Ini tidak dapat disusun dengan struktur kontrol seperti if / while / for / loop atau break / continue / return , yang harus dapat kita gunakan di dalam dan melintasi blok catch (atau dalam hal ini do ). (Ini karena notasi penuh didefinisikan dalam istilah fungsi urutan yang lebih tinggi, dan jika kita memasukkan konten dari blok catch ke dalam serangkaian penutupan bersarang, tiba-tiba aliran kontrol semua putus.)

Jadi pada akhirnya, sisi negatif dari do ini terlihat seperti notasi-do tanpa benar-benar menjadi not-not, dan tanpa jalur yang baik ke depan untuk menjadi not-not juga. Secara pribadi, saya benar-benar baik-baik saja dengan ini karena Rust tidak akan mendapatkan do notasi - tapi itu kebingungan.

@nikomatsakis , #

Apakah mungkin untuk membuat catch kata kunci kontekstual tetapi menonaktifkannya jika struct catch ada dalam lingkup, dan mengeluarkan peringatan penghentian?

Tidak yakin seberapa sesuai ini, tetapi saya mengalami masalah yang mungkin perlu diselesaikan karena terkadang Anda ingin membatalkan nilai ok daripada kesalahan yang saat ini mungkin terjadi ketika Anda menggunakan return sejauh yang Anda inginkan. batalkan kesalahan _inner_ melalui _outer_ oke. Seperti ketika sebuah fungsi mengatakan mengembalikan Option<Result<_,_>> yang cukup umum dari say an iterator.

Secara khusus saya memiliki makro dalam proyek yang saya gunakan secara berlebihan sekarang:

macro-rules! option_try {
    ( $expr:expr ) => {
        match $expr {
            Ok(x)  => x,
            Err(e) => return Some(Err(e.into())),
        }
    }
}

Sangat umum bahwa fungsi yang dipanggil dalam implementasi Iterator::next harus segera dibatalkan dengan Some(Err(e)) gagal. Makro ini bekerja di dalam tubuh fungsi normal tetapi tidak di dalam blok catch karena catch tidak mengambil return kategoris tetapi hanya sintaks khusus ? .

Meskipun pada akhirnya label-return akan membuat seluruh ide blok catch mubazir, bukan?

Sepertinya # 41414 selesai. Bisakah seseorang memperbarui OP?

Pembaruan: RFC rust-lang / rfcs # 2388 sekarang digabungkan dan catch { .. } harus diganti dengan try { .. } .
Lihat masalah pelacakan tepat di atas komentar ini.

Apa artinya ini untuk Edisi 2015, apakah sintaks do catch { .. } masih dalam jalur menuju stabilisasi, atau apakah ini akan dihapus dan hanya didukung melalui try { .. } pada Edisi 2018+?

@ Nemo77 Yang terakhir.

Apakah ada dua pernyataan try ... catch ... dalam proposal saat ini? Jika demikian, saya tidak mendapatkan semantiknya.

Bagaimanapun, jika proposal ini hanya tentang desugaring maka saya tidak keberatan. yaitu Apakah blok catch berubah di mana operator ? keluar?

Karena semua kotak centang dicentang di postingan atas, kapan kami akan melanjutkan? Jika masih ada masalah yang belum terselesaikan, kami perlu menambahkan kotak centang baru.

Mungkin ada banyak pertanyaan yang belum terselesaikan yang tidak dicatat.
Misalnya, perilaku ok-wrapping tidak diselesaikan dalam tim lang, desain Try belum diselesaikan, dan seterusnya. Kami mungkin harus membagi masalah ini menjadi beberapa masalah yang lebih bertarget karena kemungkinannya telah berakhir kegunaannya.

Hmm ... mengganggu saya karena pertanyaan-pertanyaan ini belum dicatat.

@ mark-im Jadi untuk memperjelas, saya pikir mereka telah berada di suatu tempat; tetapi tidak di satu lokasi; itu sedikit tersebar di berbagai RFC dan masalah dan semacamnya, jadi yang perlu kita lakukan adalah mencatatnya di lokasi yang tepat.

Desain sifat dukungan dilacak di https://github.com/rust-lang/rust/issues/42327; ada diskusi ekstensif di sana tentang kelemahan yang ada saat ini dan kemungkinan arah baru. (Saya berencana membuat pra-RFC untuk perubahan di sana setelah 2018 sedikit reda.)

Jadi saya pikir hanya try{} yang tersisa di sini, dan satu-satunya ketidaksepakatan yang saya ketahui untuk itu adalah hal-hal yang diselesaikan di RFC dan dikonfirmasikan kembali dalam salah satu masalah yang disebutkan di atas. Namun, mungkin bagus untuk memiliki masalah pelacakan yang berbeda.

Saya akan menambahkan kotak centang untuk satu tugas implementasi yang tertunda yang saya tahu masih perlu dilakukan ...

@scottmcm Saya tahu @joshtriplett memiliki kekhawatiran tentang pembungkusan OK (dicatat di try RFC) dan secara pribadi saya ingin membatasi break dalam stabilisasi awal try { .. } jadi bahwa Anda tidak dapat melakukan loop { try { break } } dan semacamnya.

@Sentril

sehingga Anda tidak bisa melakukan loop { try { break } }

Saat ini, Anda tidak dapat menggunakan break dalam blok non-loop, dan ini benar: break hanya boleh digunakan dalam loop. Untuk lebih awal meninggalkan blok try , cara standar adalah menulis Err(e)? . dan itu memaksa daun-daun awal selalu dalam jalur kendali "abnormal".

Jadi proposal saya adalah kode yang Anda tunjukkan harus diizinkan, dan harus melanggar loop , bukan hanya menyisakan try .

Manfaat langsungnya, adalah ketika Anda melihat break Anda tahu itu akan keluar dari perulangan, dan Anda selalu dapat menggantinya dengan continue . Juga, ini menghilangkan kebutuhan untuk memberi label break point saat menggunakan try blok di dalam loop dan Anda ingin keluar dari loop.

@Centril Terima kasih telah membesarkan mereka.

Mengenai break , secara pribadi saya akan baik-baik saja dengan hanya mengatakan bahwa try tidak peduli tentang break dan melewati loop yang berisi. Saya hanya tidak ingin break berinteraksi dengan try sama sekali.

Adapun Ok -wrapping, ya, saya ingin mengatasinya sebelum menstabilkan try .

@centril Ya, saya sadar. Tapi penting untuk diingat bahwa itu ulang kembali mengangkat masalah ini. RFC memutuskan untuk memilikinya , itu dilaksanakan tanpa itu, tapi kemudian maksud aslinya diambil _again_ , dan implementasi diubah untuk mengikuti RFC. Jadi pertanyaan besar saya adalah apakah ada fakta material yang berubah, terutama karena ini adalah salah satu topik paling berisik yang pernah saya lihat dibahas di RFC + IRLO.

@scottmcm Tentu saja, seperti yang Anda ketahui, saya setuju dengan mempertahankan Ok -wrapping;) dan saya setuju bahwa masalah tersebut harus dianggap selesai.

Saya hanya ingin mengomentari ini, tidak yakin apakah ini hal yang benar:

Pada dasarnya, situasi yang saya hadapi adalah panggilan balik dalam kerangka GUI - alih-alih mengembalikan Option atau Result , mereka perlu mengembalikan UpdateScreen , untuk memberi tahu kerangka kerja jika layar perlu diperbarui atau tidak. Seringkali saya tidak perlu masuk sama sekali (tidak praktis untuk masuk setiap kesalahan kecil) dan hanya mengembalikan UpdateScreen::DontRedraw ketika kesalahan telah terjadi. Namun, dengan operator ? , saya harus menulis ini sepanjang waktu:

let thing = match fs::read(path) {
    Ok(o) => o,
    Err(_) => return UpdateScreen::DontRedraw,
};

Karena saya tidak dapat mengonversi dari Result::Err menjadi UpdateScreen::DontRedraw melalui operator Try, ini menjadi sangat membosankan - sering kali saya memiliki pencarian sederhana di peta hash yang dapat gagal (yang bukan merupakan kesalahan ) - begitu sering dalam satu panggilan balik saya memiliki 5 - 10 penggunaan operator ? . Karena cara di atas sangat bertele-tele untuk ditulis, solusi saya saat ini adalah impl From<Result<T>> for UpdateScreen seperti ini , dan kemudian gunakan fungsi bagian dalam di callback seperti ini:

fn callback(data: &mut State) -> UpdateScreen {
     fn callback_inner(data: &mut State) -> Option<()> {
         let file_contents = fs::read_to_string(data.path).ok()?;
         data.loaded_file = Some(file_contents);
         Some(())
     }

    callback_inner(data).into()
}

Karena callback digunakan sebagai penunjuk fungsi, saya tidak dapat menggunakan -> impl Into<UpdateScreen> (untuk beberapa alasan, mengembalikan impl saat ini tidak diperbolehkan untuk penunjuk fungsi). Jadi satu-satunya cara bagi saya untuk menggunakan operator Try adalah dengan melakukan trik fungsi-dalam. Alangkah baiknya jika saya bisa melakukan sesuatu seperti ini:

impl<T> Try<Result<T>> for UpdateScreen {
    fn try(original: Result<T>) -> Try<T, UpdateScreen> {
        match original {
             Ok(o) => Try::DontReturn(o),
             Err(_) => Try::Return(UpdateScreen::DontRedraw),
        }
    }
}

fn callback(data: &mut State) -> UpdateScreen {
     // On any Result::Err, convert to an UpdateScreeen::DontRedraw and return
     let file_contents = fs::read_to_string(data.path)?;
     data.loaded_file = Some(file_contents);
     UpdateScreen::Redraw
}

Saya tidak yakin apakah ini mungkin dengan proposal saat ini dan hanya ingin menambahkan kasus penggunaan saya untuk dipertimbangkan. Akan lebih bagus jika operator Coba khusus dapat mendukung sesuatu seperti ini.

EDIT:
Saya membuat kesalahan.


Abaikan posting ini


Bisakah ini bermain lebih baik dengan tipe inferensi, itu gagal bahkan dalam kasus sederhana.

fn test_try(a: u32, b: u32) {
    let div = if b != 0 {
        Some(a / b)
    } else {
        None
    };

    let x // : Option<_> // why is this type annotation necessary
    = try { div? + 1 };

    println!("{:?}", x);
}

Jika ini ditulis ulang untuk menggunakan closure dan bukan blok try (dan dalam prosesnya longgar auto wrapping), maka kita dapatkan

fn test_closure(a: u32, b: u32) {
    let div = if b != 0 {
        Some(a / b)
    } else {
        None
    };

    let x =  (|| (div? + 1).into())();

    println!("{:?}", x);
}

Yang tidak memerlukan anotasi tipe, tetapi mengharuskan kita membungkus hasilnya.

tempat bermain

@KrishnaSannasi contoh berbasis penutupan Anda juga memiliki jenis inferensi kegagalan ( taman bermain ) karena Into tidak membatasi output dan Anda tidak menggunakannya di mana pun yang melakukannya nanti.

Ini tampaknya sebagian besar menjadi masalah dengan sifat Try daripada try blok, mirip dengan Into itu tidak menyebarkan informasi jenis apa pun dari input ke output, jadi jenis keluaran harus ditentukan oleh penggunaannya nanti. Ada _lot_ diskusi di https://github.com/rust-lang/rust/issues/42327 tentang sifat tersebut, saya belum membacanya jadi saya tidak yakin apakah ada proposal di sana yang dapat memperbaiki masalah ini.

@ Nemo

Ya, saya melakukan perubahan menit terakhir pada kode saya, untuk menggunakannya, dan tidak mengujinya. Salahku.

Seberapa jauh kita dari menstabilkan blok percobaan? Ini satu-satunya fitur yang saya butuhkan dari nightly: D

@Tokopedia

Saya yakin setelah ini selesai, itu bisa distabilkan.

blokir coba {} tangkap (atau idents berikut lainnya) untuk membiarkan ruang desain terbuka untuk masa depan, dan arahkan orang-orang ke bagaimana melakukan apa yang mereka inginkan dengan pencocokan sebagai gantinya

Apakah tidak ada desain jalan tengah untuk memungkinkan fitur sekarang sementara masih membiarkan kemungkinan untuk membiarkan ruang desain terbuka untuk masa depan (dan karena itu akhirnya catch blok)?

Humas yang saya buat harus mencentang kotak itu, CC @nikomatsakis

Saya mencoba menggunakan ini untuk pertama kalinya kemarin, dan saya sedikit terkejut bahwa ini:

#![feature(try_blocks)]
fn main() -> Result<(), ()> {
    let x: () = try {
        Err(())?
    }?;
    Ok(x)
}

tidak dapat dikompilasi karena

error[E0284]: type annotations required: cannot resolve `<_ as std::ops::Try>::Ok == _`

Sebaliknya, saya harus melakukannya

#![feature(try_blocks)]
fn main() -> Result<(), ()> {
    let x: Result<(), ()> = try {
        Err(())?
    };
    let x = x?;
    Ok(x)
}

sebagai gantinya.

Ini membingungkan pada awalnya, jadi mungkin ada baiknya mengubah pesan kesalahan atau menyebutkan dalam --explain ?

Jika Anda memindahkan tanda tanya dalam contoh pertama ke bawah sedikit

#![feature(try_blocks)]
fn main() -> Result<(), ()> {
    let x: () = try {
        Err(())?
    };
    Ok(x?)
}

Anda mendapatkan pesan kesalahan yang lebih baik. Kesalahan muncul karena Rust tidak dapat memutuskan jenis mana untuk menyelesaikan try { ... } , karena seberapa umum itu. Karena tidak dapat menyelesaikan jenis ini, ia tidak dapat mengetahui jenis <_ as Try>::Ok , itulah sebabnya Anda mendapatkan kesalahan yang Anda lakukan. (karena operator ? membuka bungkus tipe Try dan mengembalikan tipe Try::Ok ). Karat tidak dapat bekerja dengan tipe Try::Ok itu sendiri, itu harus diselesaikan melalui sifat Try , dan tipe yang menerapkan sifat itu. (yang merupakan batasan dari cara kerja pemeriksaan jenis saat ini)

Semuanya untuk fitur ini diimplementasikan, benar? Jika ya, berapa lama kita ingin melihat duduk di atas ini sebelum stabil?

Saya pikir itu masih merupakan pertanyaan terbuka apakah kami menginginkan ini atau tidak. Secara khusus, ada beberapa diskusi tentang apakah kami ingin menggunakan bahasa pengecualian di sini (coba, tangkap).

Secara pribadi, saya sangat menentang upaya untuk menciptakan kesan bahwa Rust memiliki sesuatu seperti pengecualian. Saya pikir penggunaan kata catch khususnya adalah ide yang buruk karena siapa pun yang berasal dari bahasa dengan pengecualian akan menganggap ini tidak mengikat, dan ternyata tidak. Saya berharap mengajar itu membingungkan dan menyakitkan.

Secara khusus, ada beberapa diskusi tentang apakah kami ingin menggunakan bahasa pengecualian di sini (coba, tangkap).

Saya pikir https://github.com/rust-lang/rfcs/pull/2388 secara definitif menyelesaikan apakah try sebagai nama dapat diterima. Ini bukan pertanyaan terbuka. Tetapi definisi dari sifat Try serta Ok -wrapping tampaknya seperti itu.

Ok -wrapping sudah diputuskan di RFC asli, kemudian dihapus selama implementasi, dan akhirnya ditambahkan kembali nanti. Saya tidak melihat bagaimana ini adalah pertanyaan terbuka.

@rpjohnst Yah itu karena Josh tidak setuju dengan keputusan dengan RFC asli ... :) Ini masalah yang diselesaikan bagi saya . Lihat https://github.com/rust-lang/rust/issues/31436#issuecomment -427096703, https://github.com/rust-lang/rust/issues/31436#issuecomment -427252202, dan https: // github.com/rust-lang/rust/issues/31436#issuecomment -437129491. Bagaimanapun ... inti dari komentar saya adalah bahwa try sebagai "bahasa pengecualian" adalah masalah yang sudah pasti.

Woah, kapan ini terjadi? Hal terakhir yang saya ingat adalah diskusi tentang internal. Saya juga sangat menentang Ok-wrapping :(

Eww. Tidak percaya ini terjadi. Ok -wrapping sangat buruk (merusak intuisi yang sangat masuk akal bahwa semua ekspresi yang dikembalikan dalam suatu fungsi haruslah tipe kembalian fungsi tersebut). Jadi ya, pasti dengan @ mark-im dalam hal ini. Apakah ketidaksepakatan Josh cukup untuk membuat ini menjadi masalah terbuka, dan mendapatkan lebih banyak diskusi tentang itu? Saya dengan senang hati akan memberinya dukungan dalam melawan ini, bukan berarti sebagai non-anggota tim.

Ok -wrapping seperti yang diterima di RFC 243 (secara harfiah adalah salah satu yang mendefinisikan operator ? , jika Anda bertanya-tanya kapan ini terjadi) tidak mengubah apa pun tentang jenis ekspresi pengembalian fungsi. Ini adalah bagaimana RFC 243 mendefinisikannya: https://github.com/rust-lang/rfcs/blob/master/text/0243-trait-based-exception-handling.md#catch -expressions

RFC ini juga memperkenalkan bentuk ekspresi catch {..} , yang berfungsi untuk "lingkup" operator ? . Operator catch menjalankan blok yang terkait. Jika tidak ada pengecualian yang dilempar, maka hasilnya adalah Ok(v) dimana v adalah nilai blok. Jika tidak, jika pengecualian dilemparkan, maka hasilnya adalah Err(e) .

Perhatikan bahwa catch { foo()? } pada dasarnya sama dengan foo() .

Artinya, diperlukan satu blok tipe T dan tanpa syarat membungkusnya untuk menghasilkan nilai tipe Result<T, _> . Pernyataan return di blok sama sekali tidak terpengaruh; jika blok adalah ekspresi ekor dari suatu fungsi, fungsi tersebut harus mengembalikan Result<T, _> .

Ini telah diterapkan pada malam hari selama berabad-abad: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=88379a1607d952d4eae1d06394b50959. Ini dilakukan setelah banyak diskusi oleh tim lang di, dan ditautkan dari, utas ini: rust-lang / rust # 41414 (dan ini ditautkan di bagian atas masalah ini juga).

Pada 28 Mei 2019 17:48:27 PDT, Alexander Regueiro [email protected] menulis:

Eww. Tidak percaya ini terjadi. Ok -bungkus sangat mengerikan (itu
merusak intuisi yang sangat masuk akal bahwa semua ekspresi kembali dalam a
function harus dari tipe kembalian fungsi). Jadi ya, pasti
dengan @ mark-im ini. Apakah ketidaksepakatan Josh cukup untuk membuat ini menjadi
masalah terbuka, dan dapatkan lebih banyak diskusi tentang itu? Saya dengan senang hati akan meminjamkannya dukungan
dalam melawan ini, bukan berarti sebagai non-anggota tim.

Terima kasih. Saya tidak hanya tidak setuju dengan ini hanya untuk diri saya sendiri; Saya juga mewakili banyak orang yang pernah saya lihat mengungkapkan posisi yang sama dengan saya.

@joshtriplett @ mark-im @exreg

Dapatkah salah satu dari Anda menjelaskan mengapa menurut Anda Ok wrapping sangat tidak menyenangkan atau memberikan tautan ke suatu tempat yang telah dijelaskan sebelumnya? Aku pergi mencari, tapi sepintas lalu aku tidak melihat apapun. Saya tidak punya kuda dalam hal ini (saya cukup mengomentari ini karena saya melihat semua kotak dicentang dan tidak ada diskusi selama sebulan), tetapi sekarang setelah saya menendang sarang lebah ini, saya ingin memahami argumen dengan lebih baik.

Pada Selasa, 28 Mei 2019 pukul 15.40.47 -0700, Russell Johnston menulis:

Ok -wrapping sudah diputuskan di RFC asli, kemudian dihapus selama implementasi, dan akhirnya ditambahkan kembali nanti. Saya tidak melihat bagaimana ini adalah pertanyaan terbuka.

Saya pikir Anda sebagian menjawab pertanyaan Anda sendiri. Saya tidak berpikir semua orang
terlibat dalam diskusi RFC asli ada di halaman yang sama; try tadinya
benar-benar sesuatu yang banyak orang inginkan, tapi tidak ada konsensus
untuk Ok-wrapping.

Pada Selasa, 28 Mei 2019 pukul 15.44.46 -0700, Mazdak Farrokhzad menulis:

Bagaimanapun ... inti dari komentar saya adalah bahwa try sebagai "bahasa pengecualian" adalah masalah yang sudah pasti.

Sebagai klarifikasi, menurut saya metafora "pengecualian" tidak menarik,
dan banyak upaya pada hal-hal seperti try-fn dan Ok-wrapping tampaknya berhasil
mencoba membuat bahasa palsu dengan mekanisme seperti pengecualian.
Tapi try itu sendiri, sebagai alat untuk menangkap ? selain dari
function boundary, masuk akal sebagai konstruksi aliran kontrol.

Pada Selasa, 28 Mei 2019 pukul 11:37:33 -0700, Gabriel Smith menulis:

Dapatkah salah satu dari Anda menjelaskan mengapa Anda merasa pembungkus Ok sangat tidak menyenangkan

Sebagai salah satu dari beberapa alasan:

Pada Sel, 28 Mei 2019 pukul 05:48:27 -0700, Alexander Regueiro menulis:

itu mematahkan intuisi yang sangat masuk akal bahwa semua ekspresi yang dikembalikan dalam suatu fungsi harus dari tipe kembalian fungsi tersebut

Ini mematahkan berbagai pendekatan yang digunakan orang untuk penalaran tipe diarahkan
tentang fungsi dan struktur kode.

Saya pasti punya pemikiran sendiri di sini, tetapi bisakah kita tidak membuka kembali topik ini sekarang? Kami baru saja melakukan 500+ percakapan pos yang kontroversial tentang sintaksis, jadi saya ingin menghindari ranjau darat untuk sementara waktu.

Jika ini diblokir pada tim lang yang mendiskusikannya, haruskah kotak centang "selesaikan apakah blok tangkap harus" membungkus "nilai hasil (# 41414)" dicentang lagi (mungkin dengan komentar yang diblokir pada tim lang) sehingga orang yang melihat masalah pelacakan ini tahu statusnya?

Maaf, saya tidak mencoba membuka kembali apa pun - cukup nyatakan kembali apa yang ditandai sebagai diputuskan dalam masalah pelacakan dan kapan + bagaimana itu terjadi.

@rpjohnst Terima kasih atas infonya!

@yodaldevoid Josh cukup banyak merangkum pikiran saya.

Saya sedikit kurang menentang ok-wrapping terbatas pada satu blok (berlawanan dengan mempengaruhi jenis fungsi), tapi saya pikir itu masih menetapkan preseden yang buruk: seperti yang dikatakan Josh "Saya tidak menemukan metafora pengecualian yang menarik"

@joshtriplett pada dasarnya telah meringkas pandangan saya juga: masalahnya adalah kesesuaian metafora "pengecualian" (bisa dibilang panik + catch_unwind jauh lebih mirip) dan penalaran berbasis tipe. Saya memang baik-baik saja dengan try blok sebagai mekanisme pelingkupan & aliran kontrol juga, tetapi bukan poin yang lebih radikal.

Oke, cukup adil, jangan membahas seluruh debat di sini ... mungkin hapus centang pada kotak seperti yang disarankan, dan kembalikan ke debat tim-lang (di waktu mereka sendiri), menggunakan beberapa alasan yang disebutkan di utas ini? Selama stabilisasi tidak terburu-buru, saya kira kedengarannya masuk akal.

Apakah sintaks untuk anotasi tipe telah disetujui? Saya berharap mendapatkan try { foo()?; bar()?; }.with_context(|_| failure::err_msg("foon' n' barn'")?; , yang bahkan tidak terlalu tertarik untuk mengkompilasi: error[E0282]: type annotations needed .

https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=4e60d44a8f960cf03307a809e1a3b5f2

Saya membaca komentar beberapa waktu yang lalu (dan memuat 300 komentar lagi di github terlalu membosankan), tetapi saya ingat bahwa sebagian besar (jika tidak semua) contoh mengenai perdebatan sekitar Try::Ok pembungkus digunakan Ok dalam contoh. Mengingat Option mengimplementasikan Try juga, saya ingin tahu bagaimana hal itu memengaruhi posisi tim di sisi mana perdebatan akan berlangsung.

Setiap kali saya menggunakan Rust, saya terus berpikir "man, saya benar-benar berharap dapat menggunakan blok percobaan di sini," tetapi sekitar 30% dari waktu itu karena saya benar-benar berharap dapat menggunakan try seharga Option s (seperti Saya dulu di Scala, yang menggunakan sintaks for untuk diterapkan ke monad secara umum, tetapi sangat mirip dengan try sini).

Baru hari ini, saya menggunakan json peti dan memperlihatkan metode as_* yang mengembalikan pilihan.

Menggunakan dua sintaks, contoh saya adalah:

match s {
  "^=" => |a, b| try { a.as_str()?.starts_with(b.as_str()?) }.unwrap_or(false),
  "$=" => |a, b| try { Some(a.as_str()?.ends_with(b.as_str()?)) }.unwrap_or(false),
  // original
  "$=" => |a, b| {
    a.as_str()
      .and_then(|a| b.as_str().map(|b| (a, b)))
      .map(|(a, b)| a.starts_with(b))
      .unwrap_or(false)
    },
}

Saya pikir, secara kontekstual, apakah tipe pengembaliannya adalah Option atau Result cukup jelas, dan lebih lagi, itu tidak terlalu penting (sejauh pemahaman kode berjalan). Secara transparan, artinya jelas: "Saya perlu memeriksa apakah kedua hal ini valid dan melakukan operasi padanya." Jika saya harus memilih salah satunya, saya akan memilih yang pertama, karena saya tidak percaya bahwa ada kehilangan pemahaman ketika Anda menganggap bahwa fungsi ini disematkan dalam konteks yang lebih besar, karena try akan Selalu menjadi.

Ketika saya pertama kali mulai melihat utas ini, saya menentang Ok pembungkus karena saya pikir akan lebih baik jika eksplisit, tetapi sejak itu, saya mulai memperhatikan saat-saat saya berkata "Seandainya saya bisa menggunakan blok percobaan di sini "dan saya sampai pada kesimpulan bahwa Ok -wrapping itu bagus.

Saya awalnya berpikir bahwa bukan Ok pembungkus akan lebih baik dalam kasus di mana pernyataan terakhir Anda adalah fungsi yang mengembalikan tipe yang mengimplementasikan Try , tetapi perbedaan dalam sintaksnya adalah

try {
  fallible_fn()
}

try {
  fallible_fn()?
}

Dan dalam hal ini, saya kembali berpikir Ok -wrapping lebih baik karena menjelaskan bahwa fallible_fn adalah fungsi pengembalian Try , jadi sebenarnya lebih eksplisit.

Saya ingin tahu pendapat pihak oposisi tentang hal ini, dan karena saya tidak dapat melihat banyak orang lain di utas ini, @joshtriplett.

EDIT: Saya harus menyebutkan saya hanya melihat ini dari perspektif ergonomi / pemahaman membaca. Saya tidak tahu apakah yang satu memiliki kelebihan teknis daripada yang lain dalam hal implementasi, seperti kesimpulan yang lebih mudah.

Saya ingin memberikan try kesempatan untuk beberapa Option parsing juga:

#![feature(try_blocks)]

struct Config {
    log: Option<LogConfig>,
}

struct LogConfig {
    level: Option<String>,
}

fn example(config: &Config) {
    let x: &str = try { config.log?.level? }.unwrap_or("foo");
}

Ini gagal dengan

error[E0282]: type annotations needed
  --> src/lib.rs:12:19
   |
12 |     let x: &str = try { config.log?.level? }.unwrap_or("foo");
   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type
   |
   = note: type must be known at this point

Yang paling dekat yang saya dapatkan adalah

fn example(config: &Config) {
    let x: Option<&str> = try { &**config.log.as_ref()?.level.as_ref()? };
    let x = x.unwrap_or("foo");
}

as_ref cukup disayangkan. Saya tahu bahwa Option::deref akan membantu beberapa orang di sini, tetapi tidak cukup. Ini terasa seperti ergonomi yang cocok (atau ide terkait) harus ikut bermain.

Banyak garis juga disayangkan.

Bisakah try menggunakan fallback inferensi Result seperti literal integer? Apakah ini akan membuat percobaan pertama Result<&str, NoneError> ? Masalah apa lagi yang mungkin ada- mungkin menemukan jenis kesalahan umum untuk ? s untuk dikonversi? (Apakah saya melewatkan diskusi tentang ini di suatu tempat?)

@shepmaster Saya setuju dengan jenis inferensi. Lucunya, saya mencoba kode persis Anda dengan implementasi try_ agak naif dan berfungsi dengan baik: https://github.com/norcalli/koption_macros/blob/4362fba8fa9b6c62fdaef4df30060234381141e7/src/lib.rs#L23

    let x = try_! { config.log?.level? }.unwrap_or("foo".to_owned());
    assert_eq!(x, "debug");

bekerja dengan baik.

implementasi try_ agak naif

Ya, tetapi permintaan makro Anda mengembalikan String , bukan &str , yang membutuhkan kepemilikan. Anda tidak menampilkan kode di sekitarnya, tetapi ini akan gagal karena kami tidak memiliki kepemilikan dari Config :

fn example(config: &Config) {
    let x = try_! { config.log?.level? }.unwrap_or_else(|| String::from("foo"));
}
error[E0507]: cannot move out of captured variable in an `Fn` closure
  --> src/lib.rs:20:21
   |
19 | fn example(config: &Config) {
   |            ------ captured outer variable
20 |     let x = try_! { config.log?.level? }.unwrap_or_else(|| String::from("foo"));
   |                     ^^^^^^^^^^ cannot move out of captured variable in an `Fn` closure

Ini juga tanpa syarat mengalokasikan String ; Saya menggunakan unwrap_or_else dalam contoh ini untuk menghindari ketidakefisienan itu.

Sayangnya fitur ini tidak distabilkan sebelum async/await blok. IMO akan lebih konsisten untuk dimiliki

let fut = async try {
    fut1().await?;
    fut2().await?;
    Ok(())
};

alih-alih mengizinkannya bekerja tanpa try . Tapi saya kira kapal itu sudah lama berlayar.

Re: auto-wrapping, saya rasa tidak mungkin konsisten dengan blok async sekarang. async blok melakukan "pembungkus otomatis" semacam itu, ke dalam tipe anonim yang menerapkan Future . Tetapi ini benar bahkan dengan pengembalian awal, yang tidak mungkin dilakukan dengan try blok.

Ini bisa menjadi sangat membingungkan jika kita pernah memiliki blok hipotetis async try . Haruskah itu membungkus hasil secara otomatis?

Saya tidak berpikir kami kehilangan peluang apa pun pada konsistensi dalam arti pembungkus otomatis. Baik async blok dan fungsi dapat menggunakan ? , dan keduanya harus melakukan manualnya sendiri Ok -wrapping, untuk pengembalian awal dan akhir.

Blok try , sebaliknya, dapat menggunakan ? dengan otomatis Ok -wrapping, termasuk untuk "pengembalian awal" dengan asumsi fitur pengembalian awal- mungkin label-break- nilai . Fungsi percobaan hipotetis dapat dengan mudah melakukan pembungkusan otomatis Ok pada pengembalian awal dan akhir.

Blok hipotetis async try dapat dengan mudah menggabungkan fungsionalitas dari dua- otomatis- Ok -wrap, dan kemudian otomatis- Future -wrap. (Sebaliknya tidak mungkin untuk diterapkan, dan bisa dibilang akan ditulis try async .)

Ketidakkonsistenan yang saya lihat adalah bahwa kita telah menggabungkan async blok dengan fungsi. (Ini terjadi pada menit terakhir yang bertentangan dengan RFC, tidak kurang.) Artinya adalah bahwa return dalam async blok keluar dari blok, sedangkan return dalam try blok keluar dari fungsi berisi. Namun, ini setidaknya masuk akal dalam isolasi, dan blok async tanpa pengembalian awal atau nilai pemutusan label akan jauh lebih sulit untuk digunakan.

Adakah yang menghentikan stabilisasi ini, atau belum ada yang meluangkan waktu untuk melakukannya? Saya tertarik untuk membuat PR yang diperlukan jika tidak 🙂

Pada 18 November 2019 02:03:36 AM PST, Kampfkarren [email protected] menulis:

Apa pun yang menghentikan stabilisasi ini, atau hanya tidak ada yang mengambil
waktu untuk melakukannya? Saya tertarik untuk membuat PR yang diperlukan
jika tidak 🙂>
>
->
Anda menerima ini karena Anda disebutkan.>
Balas email ini secara langsung atau lihat di GitHub:>
https://github.com/rust-lang/rust/issues/31436#issuecomment -554944079

Ya, pemblokir stabilisasi bekerja melalui keputusan tentang Ok-wrapping. Ini seharusnya tidak distabilkan sampai kita memiliki konsensus tentang bagaimana seharusnya berperilaku.

Saya pribadi menentang Ok-wrapping, tetapi saya bertanya-tanya betapa sulitnya menambahkan setelah fakta. Apakah no-ok-wrapping forward kompatibel dengan ok-wrapping?

Saya dapat membayangkan kasus-kasus rumit seperti ambiguitas Result<Result<T,E>> , tetapi dalam kasus yang ambigu seperti itu, kita bisa kembali ke no-wrapping. Pengguna kemudian bisa secara eksplisit Ok-wrap untuk menghilangkan keraguan. Sepertinya tidak terlalu buruk, karena saya tidak berharap ambiguitas semacam ini muncul terlalu sering ...

Seharusnya tidak ada ambiguitas sama sekali, karena ini bukan Ok -coercion tetapi Ok -wrapping. try { ...; x } akan menghasilkan Ok(x) sama jelasnya dengan Ok({ ...; x }) .

@joshtriplett Apakah ini belum terselesaikan? Masalah pelacakan memiliki resolve whether catch blocks should "wrap" result value sebagai dicentang, mengutip https://github.com/rust-lang/rust/issues/41414

@rpjohnst Maaf, saya seharusnya lebih jelas. Yang saya maksud adalah bahwa jika kita menstabilkan try sekarang tanpa Ok-wrapping, saya yakin itu bisa ditambahkan nanti secara backwards-compatibly.

Artinya, saya pikir kebanyakan orang setuju bahwa kita harus memiliki try blok, tetapi tidak semua orang setuju tentang catch atau ok-wrapping. Tapi menurut saya diskusi itu tidak perlu memblokir try ...

@Bayu_joo Percakapan di atas merinci perkembangan masalah ini. Itu dicentang sebelum waktunya tanpa berkonsultasi sepenuhnya dengan semua orang. @joshtriplett khususnya memiliki masalah, yang dibagikan oleh beberapa orang lain (termasuk saya).

@ mark-im Bagaimana tepatnya Anda melihat Ok-wrapping ditambahkan di masa mendatang? Saya mencoba untuk mencari tahu bagaimana itu bisa dilakukan, dan saya tidak bisa melihatnya.

Jadi saya akan mengawali ini dengan mengatakan saya tidak tahu apakah itu ide yang bagus atau tidak ...

Kami akan menstabilkan blok try tanpa pembungkus ok. Sebagai contoh:

let x: Result<usize, E> = try { 3 }; // Error: expected Result, found usize
let x: Result<usize, E> = try { Ok(3) }; // Ok (no pun intended)

Kemudian, misalkan kita sampai pada konsensus bahwa kita harus memiliki Ok-wrapping, maka kita dapat mengizinkan beberapa kasus yang sebelumnya tidak berfungsi:

let x: Result<usize, E> = try { 3 }; // Ok
let x: Result<usize, E> = try { Ok(3) }; // Also Ok for backwards compat
let x: Result<Result<usize, E1>, E2> = try { Ok(3) }; // Ok(Ok(3))
let x: Result<Result<usize, E1>, E2> = try { Ok(Ok(3)) }; // Ok(Ok(3))

Pertanyaannya adalah apakah ini dapat menyebabkan sesuatu menjadi ambigu yang sebelumnya tidak ada. Sebagai contoh:

let x = try { Err(3) }; // If x: Result<Result<T1, usize>, usize>, then it is not clear if user meant Ok(Err(3)) or Err(3)...

Meskipun, mungkin Ok-wrapping sudah harus bergulat dengan masalah ini?

Bagaimanapun, intuisi saya adalah bahwa kasus-kasus aneh seperti itu tidak sering muncul, jadi mungkin tidak terlalu penting.

Bagaimana dengan menggunakan Ok-wrapping kecuali jenis yang dikembalikan adalah Result atau Option ? Itu akan memungkinkan kode yang lebih sederhana dalam banyak kasus tetapi akan memungkinkan menentukan nilai yang tepat jika diperlukan.

// Ok-wrapped
let v: Result<i32, _> = try { 1 };

// not Ok-wrapped since the returned type is Result
let v: Result<i32, _> = try { Ok(1) };

// not Ok-wrapped since the returned type is Result
let v: Result<i32, _> = try { Err("error") };

// Ok-wrapped
let v: Option<i32> = try { 1 };

// not Ok-wrapped since the returned type is Option
let v: Option<i32> = try { Some(1) };

// not Ok-wrapped since the returned type is Option
let v: Option<i32> = try { None };

Menambahkan Ok -coercion atau sejenis ketergantungan sintaks Ok -wrapping (yang diperlukan untuk mendukung stabilisasi tanpa itu dan memperkenalkannya nanti) akan sangat buruk untuk keterbacaan, dan telah diperdebatkan secara luas beberapa kali di i.rl.o (biasanya oleh orang-orang yang salah paham tentang Ok -wrapping langsung yang diimplementasikan).

Saya pribadi sangat mendukung Ok -wrapping seperti yang diterapkan, tetapi akan lebih kuat lagi melawan segala bentuk paksaan atau ketergantungan sintaks yang membuat memahami situasi yang akan membungkus menjadi sulit (saya akan menganggap harus menulis tidak berguna Ok(...) di mana-mana karena harus mencoba dan mencari tahu apakah itu memaksa atau tidak).

let x = try { Err(3) }; // If x: Result<Result<T1, usize>, usize>, then it is not clear if user meant Ok(Err(3)) or Err(3)...

Meskipun, mungkin Ok-wrapping sudah harus bergulat dengan masalah ini?

Tidak, itu jelas Ok(Err(3)) , Ok -wrapping tidak tergantung pada sintaks atau tipe, itu hanya membungkus apa pun yang keluar dari blok dalam varian Try::Ok .

@ mark-im Saya rasa kita tidak dapat berpindah dari satu ke yang lain setelah stabilisasi. Seburuk yang saya anggap Ok-wrapping, tidak konsisten Ok-wrapping yang mencoba menebak apakah Anda menginginkannya atau tidak akan menjadi lebih buruk.

Dalam basis kode saya, saya berurusan dengan banyak nilai opsional, jadi saya memperkenalkan blok percobaan saya sendiri seperti makro sejak lama. Dan saat saya memperkenalkannya, saya memiliki berbagai varian dengan dan tanpa Ok-wrapping, dan versi Ok-wrapping ternyata jauh lebih ergonomis, sehingga itu adalah satu-satunya makro yang akhirnya saya gunakan.

Saya memiliki banyak sekali nilai opsional yang perlu saya kerjakan, dan kebanyakan numerik, jadi saya punya banyak situasi seperti ini:

let c = try { 2 * a? + b? };

Tanpa Ok-wrapping, ini akan menjadi kurang ergonomis ke titik di mana saya kemungkinan besar akan tetap menggunakan makro saya sendiri daripada menggunakan blok percobaan yang sebenarnya.

Mengingat sejarah terhormat dari masalah pelacakan ini, penggabungan aslinya dan disesalkan dengan operator ? , dan penghalang atas masalah pembungkusan Ok , saya sarankan untuk segera menutup masalah ini dan mengirimkan try kembali ke awal proses RFC, di mana diskusi ini bisa mendapatkan visibilitas yang layak dan (mudah-mudahan) mencapai semacam kesimpulan.

Tanpa Ok-wrapping, ini akan menjadi kurang ergonomis

Bisakah Anda menjelaskan tentang hal-hal non-ergonomis yang sebenarnya akan diperkenalkannya?

Tanpa Ok-wrapping, contoh Anda akan terlihat seperti:

let c = try { Ok(2 * a? + b?) };

yang menurut saya cukup bagus.

Maksud saya, dengan contoh kecil seperti ini mungkin terlihat seperti berlebihan tetapi semakin banyak kode blok try mengandung lebih sedikit dampak pembungkus Ok(...) .

Selanjutnya untuk komentar @CreepySkeleton , perlu dicatat bahwa sangat mudah untuk membuat makro yang mengemulasi Ok -wrapping jika blok try tidak melakukannya (dan seseorang pasti akan membuat kotak standar untuk makro kecil ini), tetapi sebaliknya tidak demikian.

Makro tersebut tidak mungkin dilakukan sementara sifat Try tidak stabil.

Mengapa? Bagaimanapun, ketika itu menjadi stabil (secara hipotetis tidak terlalu jauh di masa depan), itu akan sangat mungkin.

@ Nemo157 try blok juga hanya tersedia pada malam hari, dan kemungkinan besar tidak akan distabilkan jika kami memutuskan untuk merobek Try . Ini berarti kemungkinan besar tidak akan distabilkan sebelum Try . Jadi, mengatakan makro tidak mungkin tidak masuk akal.

@KrishnaSannasi Saya ingin tahu mengapa Try mungkin dicuri ?

@ mark-im Saya tidak berpikir itu akan terjadi, saya hanya menjelaskan mengapa mengkhawatirkan Try berada di malam hari untuk mencoba blok bukanlah masalah yang realistis. Saya menantikan Try pada stable.

Mengingat bahwa ? telah distabilkan, dan blok try memiliki desain yang jelas yang mencakup Result dan Option dengan cara yang sama seperti ? tidak, tidak ada alasan saya dapat melihat untuk memblokir menstabilkan mereka untuk menstabilkan Try . Saya belum mengamatinya dengan cermat, tetapi kesan saya adalah bahwa ada konsensus yang jauh lebih sedikit tentang desain Try daripada untuk try blok, jadi saya bisa melihat try blok menstabilkan tahun-tahun sebelum sifat Try (seperti yang terjadi pada ? ). Dan bahkan jika sifat Try ditinggalkan, saya tidak melihat alasan yang harus memblokir blok try distabilkan karena bekerja hanya dengan Result dan Option seperti ? kemudian akan menjadi.

(Untuk _mengapa_ Anda tidak dapat menulis makro itu dengan diberi blok try distabilkan dan sifat Try tidak stabil, makro akan berkembang menjadi try { Try::from_ok($expr) } ; Anda dapat membuat makro per jenis hanya dengan Result dan Option , tetapi IMO yang tidak memenuhi poin "sangat mudah untuk [...] ditiru").

Diberikan ? sudah stabil dalam kasing khusus meskipun sifat Try tidak dapat digunakan pada stabil, saya tidak mengerti mengapa Try tidak stabil akan memblokir blok percobaan diterapkan pada stable, karena jika sifat Try dihilangkan kita masih memiliki Opsi dan Hasil yang mendukung ? pada stabil, hanya tanpa ergonomi.

Saya akan menyarankan konsep berikut untuk mencoba menangkap semantik ...

Perhatikan kode berikut:

union SomeFunctionMultipleError {
    err0: Error1,
    err1: Error2,
}

struct SomeFunctionFnError {
    index: u32,
    errors: SomeFunctionMultipleError,
}

fn some_function() -> Result<i32, SomeFunctionFnError> {
    if 0 == 0 {
        Ok(2)
    } else {
        Err(SomeFunctionFnError{ index: 0, errors: SomeFunctionMultipleError {err1: Error2 {id0: 0, id1: 0, id3: 0}}})
    }
}

union OtherFunctionMultipleError {
    err0: Error1,
    err1: Error2,
    err2: Error3,
}

struct OtherFunctionFnError {
    id: u32,
    errors: OtherFunctionMultipleError,
}

fn other_function() -> Result<i32, OtherFunctionFnError> {
    if 0 == 0 {
        Ok(2)
    } else {
        Err(OtherFunctionFnError {id: 0, errors: OtherFunctionMultipleError {err0: Error1 {id0: 0, id1: 0}}})
    }
}

Ini adalah kode yang dapat dihasilkan oleh pengecualian Zero-Overhead di Rust dengan fitur sintaks berikut:

fn some_function() -> i32 throws Error1, Error2 {
    if 0 == 0 {
        2
    } else {
        Error2 {id0: 0, id1: 0, id3: 0}.throw
    }
}

fn other_function() -> i32 throws Error1, Error2, Error3 {
    if 0 == 0 {
        2
    } else {
        Error1{id0: 0, id1: 0}.throw
    }
}

atau bahkan kesalahan ini dapat disimpulkan oleh kompilator secara implisit:

fn some_function(i: i32) -> i32 throws { // Implicitly throws Error1, Error2
    if i == 0 {
        2
    } else if i == 1 {
        Error1 {id0: 0, id1: 0, id3: 0}.throw
    } else {
        Error2 {id0: 0, id1: 0, id3: 0}.throw
    }
}

fn other_function(i: i32) -> i32 throws { // Implicitly throws Error1
    if i == 0 {
        2
    } else {
        Error1{id0: 0, id1: 0}.throw
    }
}

Ini tidak lain gula sintaksis !! Perilakunya sama !!

Halo semua,

Adakah yang melihat proposal saya tentang pengecualian overhead-nol yang di atas?

@redradist Salah satu poin utama dari try block adalah bahwa kita dapat menggunakannya di dalam sebuah fungsi , tanpa perlu membuat fungsi untuk setiap blok. Proposal Anda sama sekali tidak terkait di sini sejauh yang saya lihat.

Baru hari ini saya merasa perlu try blok. Saya memiliki fungsi besar yang memiliki banyak operasi ? . Saya ingin menambahkan konteks ke kesalahan, tetapi melakukannya untuk setiap ? akan membutuhkan boilerplate dalam jumlah besar. Menangkap kesalahan dengan try dan menambahkan konteks di satu tempat akan mencegah hal ini.

Btw. membungkus operasi dalam fungsi dalam akan sulit dalam kasus ini, karena konteksnya tidak memiliki masa hidup yang jelas, dan membagi barang menjadi beberapa fungsi memecah NLL.

Saya ingin menyebutkan bahwa dalam implementasi saat ini blok try bukanlah ekspresi. Pikirkan bahwa ini adalah kekeliruan.

Saya ingin menyebutkan bahwa dalam implementasi saat ini blok percobaan bukanlah ekspresi. Pikirkan bahwa ini adalah kekeliruan.

Bisakah Anda memposting kode yang tidak berfungsi untuk Anda? Saya dapat menggunakannya dalam konteks ekspresi di sini: ( Rust Playground )

#![feature(try_blocks)]

fn main() {
    let s: Result<(), ()> = try { () };
}

Tentu, ini dia .

Dan ini satu lagi yang menunjukkan bahwa inferensi tipe pada blok percobaan belumlah lengkap. Yang sangat menjengkelkan karena jenis askripsi tidak didukung di if let blok.

@ Nokel81 Masalah dengan contoh Anda sebelumnya adalah bahwa ekspresi dalam if let $pat = $expr bukanlah konteks ekspresi reguler, melainkan konteks khusus "tanpa ekspresi tanda kurung". Untuk contoh bagaimana ini bekerja dengan ekspresi struct, lihat contoh ini di mana secara sintaks jelas ada ekspresi struct di sana, dan contoh ini di mana tidak. Jadi kesalahannya bukan karena try bukanlah ekspresi, tetapi kesalahannya salah, dan seharusnya mengatakan " try ekspresi tidak diperbolehkan di sini; coba tutupi dengan tanda kurung" (dan peringatan salah tentang tanda kurung yang tidak perlu disembunyikan).

Contoh terakhir Anda sebenarnya ambigu untuk jenis inferensi. Jenis e adalah _: From<usize> dalam kasus ini, yang tidak cukup informasi untuk memberikan tipe konkret. Anda perlu menggunakan beberapa cara untuk memberikan tipe konkret agar inferensi tipe berhasil. Ini bukan masalah khusus untuk try ; ini adalah cara kerja inferensi tipe di Rust.

Sekarang, jika Anda segera mencoba mencocokkan sebagai Ok dan membuang kasing Err , Anda memiliki kasus untuk pesan kesalahan suboptimal tanpa cara yang benar-benar

Ah terima kasih banyak atas penjelasannya yang mendalam. Saya rasa saya masih bingung kenapa nanti ambigu untuk jenis inferensi. Mengapa jenis ekspresi bukan Result<isize, usize> ?

Operator ? dapat melakukan konversi jenis antara jenis kesalahan yang berbeda menggunakan ciri From . Ini berkembang secara kasar ke kode karat berikut (mengabaikan sifat Try ):

match expr {
    Ok(v) => v,
    Err(e) => return From::from(e),
}

Panggilan From menggunakan jenis ekspresi akhir yang dikembalikan untuk menentukan jenis kesalahan konversi apa yang harus dilakukan, dan tidak akan default ke jenis nilai yang diteruskan secara otomatis.

Mohon maaf jika ini telah ditangani, tetapi tampaknya aneh bagi saya bahwa:

#![feature(try_blocks)]

fn main() -> Result<(), ()> {
    let result = try { // no type annotation
        Err(())?;
    };
    result.map_err(|err| err)
}

gagal untuk dikompilasi dengan:

error[E0282]: type annotations needed

tapi:

#![feature(try_blocks)]

fn main() -> Result<(), ()> {
    let result : Result<_, _> = try { // partial type annotation
        Err(())?;
    };
    result.map_err(|err| err)
}

baik-baik saja.

Jika ini adalah masalah karena argumen tipe Result tidak dapat disimpulkan, saya akan mengerti, tetapi, seperti yang ditunjukkan di atas, bukan itu masalahnya dan rustc dapat melakukan inferensi setelah diberitahu bahwa hasil dari ekspresi try adalah semacam Result , yang seharusnya dapat disimpulkan dari core::ops::Try::into_result .

Pikiran?

@nwsharp itu karena try / ? generik lebih dari Try jenis. Jika Anda memiliki jenis lain yang impl Try<Ok=_, Error=()> , blok percobaan dapat mengevaluasi ke jenis itu serta Result . Karena putus asa, teladan Anda secara kasar

#![feature(try_trait, label_break_value)]

use std::ops::Try;

fn main() -> Result<(), ()> {
    let result /*: Result<_, _>*/ = 'block: {
        match Try::into_result(Err(())) {
            Ok(ok) => Try::from_ok(ok),
            Err(err) => {
                break 'block Try::from_error(From::from(err));
            }
        }
    };
    result.map_err(|err| err)
}

@ CAD97 Terima kasih atas penjelasannya.

Yang mengatakan, saya tidak berharap bahwa try akan secara efektif dapat menyebabkan semacam konversi antara impls Try berbeda.

Saya mengharapkan de-surgaring di mana impl Try sama dipilih untuk into_result , from_ok , dan from_error .

Menurut pendapat saya, kerugian ergonomis karena tidak dapat melakukan inferensi tipe (terutama mengingat tidak ada alternatif impl Try bahkan ada), tidak lebih besar daripada manfaat dari memungkinkan konversi ini.

Kami dapat mengizinkan inferensi dengan menghilangkan ambiguitas, dan mempertahankan kemampuan untuk ikut serta dalam konversi melalui sesuatu seperti:

try { ... }.into()

Dengan implisit selimut yang sesuai:

impl<T: Try, E: Into<T::Err>> From<Result<T::Ok, E>> for T {
    fn from(result: Result<T::Ok, E>) -> Self {
        match result {
            Ok(ok) => T::from_ok(ok),
            Err(err) => T::from_err(err.into()),
        }
    }
}

(Yang sejujurnya saya kira masuk akal memiliki apa pun, meskipun saya secara pribadi meragukan konversi otomatis jenis kesalahan di sini. Jika diinginkan, pengguna harus .map_err() pada Result .)

Secara umum, menurut saya de-sugaring ini "terlalu pintar". Itu terlalu banyak menyembunyikan dan semantiknya saat ini cenderung membingungkan orang. (Terutama mengingat bahwa implementasi saat ini meminta anotasi jenis pada sesuatu yang tidak mendukungnya secara langsung!)

Atau, lebih jauh lagi dengan implan selimut, saya kira.

impl <T: Try, U: Try> From<U> for T 
    where U::Ok : Into<T::Ok>, U::Err : Into<T::Err>
{
    fn from(other: U) -> Self {
        match other.into_result() {
            Ok(ok) => Self::from_ok(ok.into()),
            Err(err) => Self::from_err(err.into()),
        }
    }
}

Atau terserah...

Yang mengatakan, saya tidak berharap bahwa percobaan akan secara efektif dapat menyebabkan semacam konversi antara impls Try berbeda.

Saya mengharapkan pembedahan di mana impl Try sama dipilih untuk into_result , from_ok , dan from_error .

Menurut pendapat saya, kerugian ergonomis karena tidak dapat melakukan jenis inferensi (terutama mengingat tidak ada alternatif impl Try bahkan ada), tidak lebih besar daripada manfaat dari memungkinkan konversi ini.

Ada empat tipe stabil Try : Option<T> , Result<T, E> , Poll<Result<T, E>> , dan Poll<Option<Result<T, E>> .

NoneError tidak stabil, jadi Option<T> macet saat mencoba Option<T> sedangkan NoneError tidak stabil. (Perhatikan bahwa dokumen secara eksplisit memanggil From<NoneError> sebagai "aktifkan option? ke jenis kesalahan Anda.")

Poll impls, bagaimanapun, mengatur jenis kesalahan mereka ke E . Karena itu, "type morphing" dari Try stabil, karena Anda dapat ? a Poll<Result<T, E>> dalam -> Result<_, E> untuk mendapatkan Poll<T> dan kembalikan kasing E .

Faktanya, ini memberi kekuatan pada pembantu kecil yang "imut" :

fn lift_err<T, E>(x: Poll<Result<T, E>>) -> Result<Poll<T>, E> { Ok(x?) }

@ CAD97 Terima kasih telah menghibur saya. Ini akan menjadi hal yang sulit untuk diajarkan kepada pendatang baru dan akan membutuhkan sedikit cinta dalam hal pesan kesalahan.

Pernahkah dipikirkan untuk memungkinkan spesifikasi impl Try diinginkan untuk meredakan perilaku tidak intuitif di sini?

Misalnya, bikeshedding sebentar, try<T> { ... } . Atau adakah lagi sesuatu yang bisa membuat Anda tersandung dengan hal itu?

Untuk mungkin menambahkan sedikit lebih banyak warna di sini, fakta bahwa try { } lebih dari sekumpulan Result tidak "hanya" menghasilkan Result tidak terduga dan membuat saya sedih . Saya mengerti mengapa , tapi saya tidak menyukainya.

Ya, telah ada pembahasan tentang kombinasi "tipe anggapan umum" (ada istilah yang Anda cari) dan try . Saya pikir, terakhir saya dengar, try: Result<_, _> { .. } pada akhirnya dimaksudkan untuk bekerja.

Tetapi saya setuju dengan Anda: try blok layak mendapatkan beberapa diagnostik yang ditargetkan untuk memastikan jenis keluarannya ditentukan.

Lihat masalah terpisah ini untuk pertanyaan sempit tertentu guna menyelesaikan konsensus tim bahasa tentang masalah Ok -wrapping.

Harap baca komentar pembuka utas itu sebelum berkomentar, dan khususnya, harap diperhatikan bahwa utas itu hanya tentang satu pertanyaan itu, bukan masalah lain yang terkait dengan try atau ? atau Try .

Saya tidak mengerti mengapa blok try diperlukan. Sintaks ini

fn main() -> Result<(), ()> {
    try {
        if foo() {
            Err(())?
        }
        ()
    }
}

bisa diganti dengan ini:

fn main() -> Result<(), ()> {
    Ok({
        if foo() {
            Err(())?
        }
        ()
    })
}

Keduanya menggunakan jumlah karakter yang sama, tetapi karakter kedua sudah stabil.

Saat menetapkan hasil ke variabel, itu mungkin menunjukkan fungsi pembantu harus dibuat untuk mengembalikan hasilnya. Jika itu tidak memungkinkan, closure bisa digunakan.

@dylni try blok sangat berguna ketika mereka tidak berisi keseluruhan fungsi. Operator ? pada kesalahan membuat kontrol aliran pergi ke akhir blok try , tanpa kembali dari fungsi.

`` karat
fn main () / * tidak ada hasil di sini * / {
biarkan hasilnya = coba {
foo () ?. bar () ?. baz ()?
};
hasil pertandingan {
//…
}
}

@SimonSapin Apakah itu sering muncul? Saya jarang mengalami situasi yang masuk akal, dan biasanya ada cara yang baik untuk mengatasinya. Dalam contoh Anda:

fn main() /* no result here */ {
    let result  = foo()
        .and_then(|x| x.bar())
        .and_then(|x| x.baz());
    match result {
        // …
    }
}

Itu lebih bertele-tele, tetapi saya pikir solusi yang lebih sederhana adalah sintaks penutupan metode:

fn main() /* no result here */ {
    let result  = foo()
        .and_then(::bar)
        .and_then(::baz);
    match result {
        // …
    }
}

Jenis ini juga disimpulkan dengan benar dengan and_then , di mana Anda memerlukan penjelasan jenis untuk try . Saya pernah mengalami ini cukup jarang sehingga saya tidak berpikir sintaks yang lebih pendek akan sepadan dengan bahaya keterbacaan.

RFC yang diterima memiliki beberapa alasan lain: https://rust-lang.github.io/rfcs/0243-trait-based-exception-handling.html

Bagaimanapun, argumen yang mendukung konstruksi bahasa untuk aliran kontrol dengan operator ? (dan .await ) atas metode chaining seperti and_then telah dibahas secara ekstensif.

Bagaimanapun, argumen yang mendukung konstruksi bahasa untuk aliran kontrol dengan operator ? (dan .await ) atas metode chaining seperti and_then telah dibahas secara ekstensif.

@SonSapin Terima kasih. Ini dan membaca ulang RFC meyakinkan saya bahwa ini dapat bermanfaat.

Saya pikir saya bisa menggunakan blok percobaan untuk dengan mudah menambahkan konteks ke kesalahan, tetapi sejauh ini tidak berhasil.

Saya menulis fungsi kecil yang berfungsi dengan baik. Perhatikan bahwa File::open()? gagal dengan std::io::Error sedangkan baris berikut gagal dengan anyhow::Error . Meskipun jenisnya berbeda, kompilator mencari cara untuk mengonversi keduanya menjadi Result<_, anyhow::Error> .

fn tls_add_cert(config: &ClientConfig, path: impl AsRef<Path>) -> Result<(usize, usize), anyhow::Error> {
    let path = path.as_ref();
    let mut file = BufReader::new(File::open(path)?);
    Ok(config.root_store.add_pem_file(&mut file)
        .map_err(|_| anyhow!("Bad PEM file"))?)
}

Saya ingin menambahkan beberapa konteks kesalahan jadi saya mencoba menggunakan blok percobaan dan bagaimanapun juga with_context() :

fn tls_add_cert(config: &ClientConfig, path: impl AsRef<Path>) -> anyhow::Result<(usize, usize)> {
    let path = path.as_ref();
    try {
        let mut file = BufReader::new(File::open(path)?);
        Ok(config.root_store.add_pem_file(&mut file)
            .map_err(|_| anyhow!("Bad PEM file"))?)
    }
    .with_context(|| format!("Error adding certificate {}", path.display()))
}

Tapi sekarang inferensi tipe gagal:

error[E0282]: type annotations needed
  --> src/net.rs:29:5
   |
29 | /     try {
30 | |         let mut file = BufReader::new(File::open(path)?);
31 | |         Ok(config.root_store.add_pem_file(&mut file)
32 | |             .map_err(|_| anyhow!("Bad PEM file"))?)
33 | |     }
   | |_____^ cannot infer type
   |
   = note: type must be known at this point
   ```

I don't understand why a type annotation is needed here but not in the first case. Nor do I see any easy way to add one, as opposed to using an [IIFE](https://en.wikipedia.org/wiki/Immediately_invoked_function_expression) which does let me add an annotation:

```rust
(|| -> Result<_, anyhow::Error> {
    let domain = DNSNameRef::try_from_ascii_str(host)?;
    let tcp = TcpStream::connect(&(host, port)).await?;

    Ok(tls.connect(domain, tcp).await?)
})()
.with_context(|| format!("Error connecting to {}:{}", host, port))

@jodohgratis

Lagi,

itu karena try / ? bersifat umum lebih dari Try jenis. Jika Anda memiliki jenis lain yang [ impl Try<Ok=_, Error=anyhow::Error> ], blok percobaan dapat mengevaluasi ke jenis itu serta Result .

(Selain itu, Anda tidak perlu Ok ekspresi tambahan Anda di blok try (# 70941).)

Saya pikir fakta bahwa ini terus muncul berarti itu

  • Sebelum stabilisasi, try perlu mendukung jenis ascription ( try: Result<_,_> { atau apa pun) atau mengurangi masalah ini,
  • Ini pasti membutuhkan diagnosa yang ditargetkan ketika jenis inferensi dari blok try gagal, dan
  • Kita harus mempertimbangkan untuk memberikan try jenis fallback ke Result<_,_> jika tidak dibatasi. Ya, itu sulit, kurang ditentukan, dan berpotensi bermasalah, tetapi _would_ menyelesaikan 80% kasus try blok membutuhkan anotasi tipe karena $12: Try<Ok=$5, Error=$8> tidak cukup spesifik.

Selain itu, mengingat # 70941 tampaknya menyelesaikan ke arah "ya, kami ingin (beberapa bentuk) ' Try::from_ok pembungkus'", kami mungkin _also_ menginginkan diagnostik yang ditargetkan untuk saat ekspresi ekor try block mengembalikan Ok(x) ketika x akan berfungsi.

Saya menduga bahwa perilaku yang tepat untuk dicoba adalah

  • perpanjang sintaks untuk mengizinkan askripsi manual seperti try: Result<_, _> { .. } , try as Result<> , atau apa pun (menurut saya try: Result mungkin baik-baik saja? sepertinya sintaks yang lebih disukai)
  • periksa "jenis yang diharapkan" yang berasal dari konteks - jika ada, lebih baik sebagai jenis hasil dari try
  • jika tidak, default ke Result<_, _> - ini bukan jenis inferensi fallback seperti dengan i32 , itu akan terjadi lebih awal, tetapi itu berarti bahwa hal-hal seperti try { }.with_context(...) kompilasi.

Namun, saya khawatir bahwa kita mungkin mendapatkan kesalahan sekitar ? dan pemaksaan into , setidaknya selama jenis kesalahan tidak ditentukan. Khususnya jika Anda menulis kode di mana Anda ? hasil dari blok try , seperti ini:

#![feature(try_blocks)]

use std::error::Error;
fn foo() -> Result<(), Box<dyn Error>> {
    let x: Result<_, _> = try {
        std::fs::File::open("foo")?;
    };

    x?;

    Ok(())
}

fn main() { 
}

Anda masih mendapatkan kesalahan ( playground ) dan memang demikian, karena tidak jelas di mana ? pemaksaan "menjadi" harus dipicu.

Saya tidak yakin apa solusi terbaik di sini tetapi mungkin melibatkan beberapa jenis fallback inferensi yang akan membuat saya gugup.

mungkin melibatkan beberapa jenis fallback inferensi yang akan membuat saya gugup.

Yang paling sederhana: jika semua penggunaan ? dalam blok Try berisi tipe Try::Error , gunakan jenis kesalahan itu untuk blok try (kecuali jika tidak terikat).

"(Kecuali terikat)", tentu saja, adalah bagian halus yang menakutkan.

Saya harap saya tidak terlalu tidak membangun dengan posting ini. Namun, saya ingin membandingkan contoh @nikomatsakis dengan contoh dari dunia paralel di mana ? tidak melakukan konversi paksa, dan tidak ada pembungkus otomatis dari hasil blokir try :

use std::error::Error;
fn foo() -> Result<(), Box<dyn Error>> {
    let x = try {
        std::fs::File::open("foo").err_convert()?;
        Ok(())
    };

    x?;

    Ok(())
}

Di dunia ini:

  • Sangat mudah untuk melihat bahwa lingkup try dan fn itu sendiri menghasilkan kesuksesan tanpa nilai apa pun. Juga mudah dilihat bahkan tanpa mencoba bahwa mereka menghasilkan Result s.
  • Jelas di mana konversi kesalahan terjadi.
  • Konversi kesalahan dapat dipindahkan ke ekspresi x? , membuat cakupan try khusus untuk operasi std::fs::File .
  • Semua petunjuk tipe berantai dengan lancar dari tanda tangan tipe. Baik untuk mesin maupun untuk kita manusia.
  • Jenis yang mengisyaratkan pengguna hanya diperlukan dalam kasus di mana kita benar-benar ingin melipat kesalahan ke kesalahan lain yang independen.

Saya akan sangat bahagia di alam semesta paralel itu.

@phaylon Sementara saya menghargai cara hati-hati Anda menulis komentar itu, aku takut itu agak tidak konstruktif. Konversi kesalahan adalah bagian dari ? dan itu tidak akan berubah, dan, mengingat itu, ok-wrapping pada dasarnya ortogonal dari sisa diskusi ini.

Jika try functions (dengan tipe return dan throw) pernah dipertimbangkan, maka mungkin ada baiknya juga mempertimbangkan sintaks untuk menganggap blok try menjadi sesuatu yang serupa.

misalnya

try fn foo() -> u32 throw String {
  let result = try: u32 throw String {
    123
  };
  result?
}

Maaf jika ini sudah dibahas tapi apa keuntungan menggunakan

try fn foo() -> u32 throw String { ... }

atau serupa dengan

fn foo() -> Result<u32, String> { ... }

?
Sepertinya sintaks duplikat.

@gorilskij Seperti yang saya pahami, keuntungan utama adalah mendapatkan Ok -wrapping. Jika tidak, Anda harus menulis:

fn foo() -> Result<u32, String> {
    try {
        // function body
    }
}

Beberapa orang juga lebih suka throws karena mereka menganggap terminologi pengecualian berhubungan.

Secara pribadi, saya ingin menghindari munculnya pengecualian.

Ini bukan utas untuk membahas try fn , jadi tolong jangan mengambil garis singgung ini lebih jauh. Utas ini untuk fitur yang diterima dari try blok, bukan fitur potensial (dan sampai sekarang, bukan RFCd) try fn .

Saya baru saja memperhatikan bahwa RFC asli untuk ? diusulkan menggunakan Into , bukan From . Ini menyatakan:

RFC saat ini menggunakan sifat std::convert::Into untuk tujuan ini (yang memiliki penyampaian implantasi selimut dari From ).

Meskipun meninggalkan metode upcasting yang tepat sebagai pertanyaan yang belum terselesaikan . Into (mungkin) lebih disukai berdasarkan panduan dari From :

Lebih suka menggunakan Into daripada menggunakan From saat menentukan batas sifat pada fungsi generik. Dengan cara ini, tipe yang secara langsung mengimplementasikan Into juga dapat digunakan sebagai argumen.

Namun, dalam RFC ciri Try , Into tidak lagi disebutkan, dan konversi dilakukan menggunakan From sebagai gantinya. Ini juga yang sekarang digunakan oleh kode, bahkan untuk ?
https://github.com/rust-lang/rust/blob/b613c989594f1cbf0d4af1a7a153786cca7792c8/src/librustc_ast_lowering/expr.rs#L1232

Hal ini tampaknya tidak menguntungkan, karena tidak ada implementasi menyeluruh dari Into menjadi From . Ini berarti bahwa kesalahan yang mengimplementasikan Into (dibandingkan dengan From ) baik karena kesalahan, karena alasan lama, atau karena beberapa kebutuhan lain, tidak dapat digunakan dengan ? (atau Try ). Ini juga berarti bahwa implementasi apa pun yang mengikuti rekomendasi di pustaka standar untuk menggunakan Into dalam batas tidak dapat menggunakan ? . Dengan sebuah contoh, perpustakaan standar merekomendasikan saya untuk menulis:

fn with_user_err<E>(op: impl Fn() -> Result<(), E>) -> Result<(), MyError>
where E: Into<MyError>

tetapi jika saya melakukannya, saya tidak dapat menggunakan ? di badan fungsi. Jika saya ingin melakukan itu, saya harus menulis

fn with_user_err<E>(op: impl Fn() -> Result<(), E>) -> Result<(), MyError>
where MyError: From<E>

tetapi jika saya melakukannya, pengguna dengan jenis kesalahan yang menerapkan Into alih-alih From tidak dapat menggunakan fungsi ini. Perhatikan bahwa kebalikannya tidak benar, karena implan selimut Into berdasarkan From .

Mungkin (?) Terlambat untuk memperbaiki ? sekarang (yang _very_ disayangkan - mungkin di edisi berikutnya?), Tetapi setidaknya kita harus memastikan untuk tidak menggali lebih dalam jalur itu di Try sifat.

@jonhoo @cuviper mencoba mengubah From menjadi Into di # 60796 untuk memeriksa # 38751 dan itu mengakibatkan sejumlah besar kerusakan inferensi persis karena From -> Into blanket impl mempersulit rustc untuk menangani kasus umum dari konversi identitas. Diputuskan bahwa tidak sebanding dengan biaya yang dikeluarkan untuk memecahkan kesimpulan.

@jonhoo Anda mungkin juga menemukan komentar ini dari niko informatif:

Ada juga batasan kode keras dalam sistem sifat. Jika Anda harus menyelesaikan tujuan seperti ?X: Into<ReturnType> , kami akan gagal menyelesaikannya, tetapi jika Anda harus menyelesaikan tujuan seperti ReturnType: From<?X> , kami berpotensi berhasil dan menyimpulkan nilai ?X .

Edit: Di sini, ?X mengacu pada beberapa variabel inferensi yang tidak diketahui. Batasan kode keras dalam sistem sifat saat ini adalah bahwa jenis Self setidaknya harus disimpulkan sebagian agar kita dapat menjelajahi opsi itu.

TL; DR adalah menyimpulkan dengan Into lebih sulit, dan dengan cara yang melekat pada cara fungsi pemecah sifat.

@KrishnaSannasi @ CAD97 Terima kasih, itu membantu! Saya masih khawatir tentang kami terlalu banyak menstabilkan yang didasarkan pada From mengingat bahwa kami meninggalkan pelaksana Into semacam secara permanen. Apakah harapan bahwa kesimpulan di sini pada akhirnya akan menjadi lebih baik? Haruskah panduan untuk memilih Into dalam batas-batas diubah? Apakah kita mengharapkan bahwa dengan aturan koherensi sifat baru di 1.41 (saya pikir itu) tidak ada lagi alasan untuk mengimplementasikan hanya Into , dan mempertimbangkan semua bug impls itu?

Jika kesimpulan sudah cukup bagus, itu harus kompatibel ke depan untuk mengubah ke Into nanti. Yang terburuk yang bisa kita pecahkan adalah kesimpulan yang diberikan bahwa From berarti Into

Apakah ini (penutup Try sifat) memungkinkan ? bekerja dengan ciri Into / From dengan batas umum untuk jenis yang menerapkan Try (mis. Result itu sendiri)?

yaitu ? dalam penutupan atau fungsi yang mengembalikan misalnya impl Into<Result>

(Sepertinya tidak ketika saya mencobanya pada malam hari?)

Saya ingin melihat ini stabil. Setelah membaca utas ini, dan # 70941, saya pikir ringkasannya harus diperbarui sebagai berikut:

  1. "selesaikan apakah blok tangkapan harus" membungkus "nilai hasil" harus dicentang, "Diselesaikan sebagai ya "

  2. Perhatian baru ditambahkan tentang masalah inferensi ini. Mungkin sesuatu seperti:

    • [] Kesulitan ergonomis karena masalah dengan jenis inferensi.

ISTM yang menjadi perhatian terakhir ini dapat ditangani dengan, antara lain:

  • Tambahkan sintaksis enkripsi jenis dipesan lebih dahulu ke try , sebelum stabilisasi
  • Putuskan bahwa https://github.com/rust-lang/rfcs/pull/803 # 23416 (Ketik askripsi) akan mengatasi ini dan harus distabilkan terlebih dahulu
  • Tambahkan beberapa jenis penggantian otomatis (mis. Ke Result , mungkin seperti yang disarankan di https://github.com/rust-lang/rust/issues/31436#issuecomment -614735806
  • Putuskan untuk menstabilkan try apa adanya, sekarang, tinggalkan ruang sintaksis / semantik untuk perbaikan di masa mendatang

(Beberapa dari opsi ini tidak saling eksklusif.)

Terima kasih atas perhatian Anda dan saya harap pesan ini bermanfaat bagi Anda.

Jenis anggapan memiliki sejumlah masalah (sintaksis dan lainnya), dan tampaknya tidak mungkin segera diterapkan, apalagi distabilkan; memblokir try blok pada sintaks tipe askripsi tampaknya tidak sesuai.

Sebuah fallback ke Result mungkin membantu, tapi tidak menyelesaikan masalah inferensi tipe dengan tipe error: try { expr? }? (atau dalam prakteknya setara yang lebih kompleks) memiliki dua panggilan efektif ke .into() , yang memberi kompilator terlalu banyak fleksibilitas pada tipe perantara.

@ijackson terima kasih telah mengambil inisiatif untuk meringkas keadaan saat ini. Saya pikir Anda benar bahwa ada berbagai cara yang dapat kami tingkatkan pada blok percobaan, tetapi salah satu masalahnya adalah kami tidak yakin mana yang harus dilakukan, sebagian karena setiap solusi memiliki kekurangan uniknya sendiri.

Berkenaan dengan jenis ascription, saya merasa tantangan implementasinya tidak terlalu sulit. Itu mungkin kandidat yang baik untuk diperhatikan dan mencoba mendorongnya ke garis akhir. Saya tidak ingat apakah ada banyak kontroversi tentang sintaks atau semacamnya.

Pada Rabu, 05 Agustus 2020 pada 14:29:06 -0700, Niko Matsakis menulis:

Berkenaan dengan jenis ascription, saya merasa tantangan implementasinya tidak terlalu sulit. Itu mungkin kandidat yang baik untuk diperhatikan dan mencoba mendorongnya ke garis akhir. Saya tidak ingat apakah ada banyak kontroversi tentang sintaks atau semacamnya.

Seingat saya, perhatian utama adalah mengizinkan anggapan tipe
di mana-mana akan terjadi perubahan tata bahasa yang substansial, dan berpotensi a
membatasi satu. Saya tidak ingat detail lengkapnya, hanya yang menjadi perhatiannya
dibesarkan.

Secara pribadi menurut saya masalah ergonomis tidak terlalu buruk sehingga tidak ada gunanya menstabilkan fitur ini sekarang. Bahkan tanpa asumsi tipe ekspresi, memperkenalkan pengikatan biarkan bukanlah solusi yang buruk.

Juga, try blok mungkin berguna di makro. Secara khusus, saya bertanya-tanya apakah @withoutboats pustaka fehler yang sangat baik akan mengalami lebih sedikit masalah dengan kekurangan dalam sistem makro kita, jika itu dapat membungkus tubuh procs dalam try .

Saya menemukan tempat-tempat di mana saya ingin sekali menggunakan blok percobaan. Akan lebih baik jika ini melewati batas. Secara pribadi, saya benar-benar akan, 100% jenis pengorbanan anggapan jika diperlukan untuk mencoba blok melewati garis. Saya belum menemukan diri saya dalam situasi di mana saya berkata "dang saya akan senang memiliki jenis ascription di sini," tetapi akhirnya melakukan IIFE untuk mensimulasikan banyak blok percobaan. Membiarkan fitur yang berguna dan berjangka panjang tidak stabil karena bertentangan dengan fitur jangka panjang yang tidak stabil adalah situasi yang sangat disayangkan.

Untuk lebih spesifik, saya menemukan diri saya melakukan ini ketika saya berada di dalam fungsi yang mengembalikan Hasil, tetapi saya ingin melakukan semacam pemrosesan pada hal-hal yang mengembalikan Opsi. Yang mengatakan, jika Try secara umum stabil, saya mungkin masih lebih suka blok percobaan, karena saya sebenarnya tidak ingin kembali dari fungsi utama untuk melakukannya, tetapi sebaliknya, berikan semacam nilai default jika ada yang ada di rantai Tidak ada. Ini cenderung terjadi pada saya dalam kode gaya serialisasi.

Secara pribadi, saya lebih sering ingin mengetik ascription daripada mencoba blok (meskipun saya menginginkan keduanya). Secara khusus, saya sering kesulitan dengan "tipe debugging" di mana kompiler menyimpulkan tipe yang berbeda dari yang saya harapkan. Biasanya, Anda harus menambahkan let baru yang mengikat di suatu tempat, yang benar-benar mengganggu dan menyebabkan rustfmt memecahkan riwayat pembatalan. Selain itu, ada banyak tempat di mana jenis anggapan akan menghindari ikan turbo ekstra.

Sebaliknya, saya hanya dapat menggunakan and_then atau kombinator lain untuk menghentikan lebih awal tanpa keluar. Mungkin tidak sebersih blok percobaan, tapi tidak seburuk itu juga.

@steveklabnik @ mark-im Coba blok dan askripsi jenis tidak bertentangan, ini bukan pertanyaan tentang satu fitur atau lainnya. Hanya try blok memiliki kegagalan inferensi tipe yang tidak ergonomis, dan asumsi tipe umum dapat menjadi cara untuk memecahkan masalah tersebut, tetapi karena tipe ascription yang digeneralisasi bukanlah fitur jangka pendek atau bahkan hal yang pasti, @joshtriplett (dan saya setuju) tidak ingin fitur ini memblokir terjadinya anggapan tipe umum.

Ini bahkan tidak berarti kita tidak akan membuat tipe anggapan umum sebagai solusi untuk masalah; Salah satu pilihan yang perlu diselidiki adalah "menstabilkan percobaan sebagaimana adanya, mengharapkan bahwa suatu saat asumsi tipe umum akan menyelesaikan masalah itu." Semua yang dikatakan adalah jangan menghalangi stabilisasi pada jenis anggapan.


@ rust-lang / lang Saya harus mengakui agak sulit untuk memahami nuansa kegagalan inferensi tipe dari utas ini, karena keterbatasan GitHub dan banyak subjek lain yang dibahas di sini. Karena mencapai keputusan tentang bagaimana menangani kegagalan inferensi adalah satu-satunya hal yang menghalangi upaya untuk menstabilkan, saya pikir akan bermanfaat jika kita mengadakan pertemuan untuk membahas hal ini, dan seseorang dapat mengambil poin untuk mendapatkan pemahaman yang mendalam tentang masalah inferensi .

Satu pertanyaan yang muncul pada saya, misalnya: apakah ini masalah kesimpulan secara khusus karena fleksibilitas konversi yang kami izinkan di Try ? Saya tahu keputusan itu telah dibicarakan sampai mati, tetapi jika demikian, ini tampak seperti informasi baru yang relevan yang dapat membenarkan perubahan definisi sifat Try .

@withoutboats Saya setuju dengan kebutuhan untuk mengumpulkan semua informasi di satu tempat dan keinginan untuk mendorong fitur ini melewati garis finis. Yang mengatakan, saya pikir terakhir kali kami menyelidiki di sini juga menjadi jelas bahwa perubahan ke Try mungkin sulit karena kompatibilitas mundur - @cramertj menyebutkan beberapa Pin impls, IIRC.

Apakah halaman ini membantu?
0 / 5 - 0 peringkat