Apa perbedaan antara Meningkatkan Pengecualian vs Melempar Pengecualian di Ruby?

168

Ruby memiliki dua mekanisme pengecualian yang berbeda: Throw / Catch dan Naikkan / Rescue.

Kenapa kita punya dua?

Kapan sebaiknya Anda menggunakan satu dan bukan yang lain?

Nick Retallack
sumber
"Keluar dari loop bersarang" adalah kebutuhan umum dalam banyak bahasa pemrograman. Selain gotodi C / C ++ seperti @docwhat telah disebutkan, Java telah memberi label break dan continue . (Python juga memiliki proposal yang ditolak untuk ini.)
Franklin Yu

Jawaban:

104

Saya pikir http://hasno.info/ruby-gotchas-and-caveats memiliki penjelasan yang layak tentang perbedaan:

menangkap / melempar tidak sama dengan menaikkan / menyelamatkan. catch / throw memungkinkan Anda untuk dengan cepat keluar dari blok kembali ke titik di mana tangkapan didefinisikan untuk simbol tertentu, meningkatkan penyelamatan adalah pengecualian nyata yang menangani hal-hal yang melibatkan objek Pengecualian.

Hanya baca
sumber
1
Penasaran ingin tahu ... Membaca ini dari iPad, jadi tidak bisa mengujinya di 1.9, tetapi beberapa gotcha itu tidak lagi berlaku di versi ruby ​​terbaru, kan?
Denis de Bernardy
12
Juga perlu diketahui: raisesangat mahal. throwtidak. Anggap throwsebagai menggunakan gotountuk keluar dari lingkaran.
docwhat
4
@Denis Gotchas mana yang Anda maksud?
docwhat
1
Tautan rusak!
morhook
Lihat ruby catch-throw dan efisiensi untuk memahami lebih lanjut tentang perbedaan kinerja.
Franklin Yu
110
  • raise, fail, rescue, Dan ensuremenangani kesalahan , juga dikenal sebagai pengecualian
  • throwdan catchyang aliran kontrol

Tidak seperti dalam bahasa lain, lemparan dan tangkapan Ruby tidak digunakan untuk pengecualian. Sebaliknya, mereka menyediakan cara untuk menghentikan eksekusi lebih awal ketika tidak ada pekerjaan lebih lanjut diperlukan. (Grimm, 2011)

Mengakhiri satu tingkat aliran kontrol, seperti whileloop, dapat dilakukan dengan sederhana return. Mengakhiri banyak level aliran kontrol, seperti loop bersarang, dapat dilakukan dengan throw.

Meskipun mekanisme pengecualian untuk meningkatkan dan menyelamatkan sangat bagus untuk mengabaikan eksekusi ketika ada kesalahan, terkadang bagus untuk dapat melompat keluar dari beberapa konstruksi yang bersarang selama pemrosesan normal. Di sinilah menangkap dan melempar berguna. (Thomas and Hunt, 2001)

Referensi

  1. Grimm, Avdi. "Lempar, Tangkap, Angkat, Selamatkan ... Aku sangat Bingung!" RubyLearning Blog. Np, 11 Juli 2011. Web. 1 Januari 2012. http://rubylearning.com/blog/2011/07/12/throw-catch-raise-rescue--im-so-confused/ .
  2. Thomas, Dave, dan Andrew Hunt. "Memprogram Ruby." : Panduan Programmer Pragmatis. Np, 2001. Web. 29 September 2015. http://ruby-doc.com/docs/ProgrammingRuby/html/tut_exceptions.html .
Jared Beck
sumber
2
Avdi sepertinya tidak terdengar di podcast.
hrdwdmrbl
2
Tautan Ruby Learning sepertinya tidak berfungsi. Berikut posting blog lain yang membahas perbedaan: danielchangnyc.github.io/blog/2013/10/23/throw-raise
Dennis
Lucu, rubylearning.com berpikir bahwa artikel Avdi masih ada di sana . Saya rasa itu sebabnya kami menyalin barang ke SO, jadi tidak akan hilang!
Jared Beck
21

https://coderwall.com/p/lhkkug/don-t-confuse-ruby-s-throw-statement-with-raise menawarkan penjelasan yang luar biasa yang saya ragu bisa saya tingkatkan. Untuk meringkas, menandai beberapa contoh kode dari posting blog saat saya pergi:

  1. raiseSaya rescueadalah analog terdekat dengan throw/ catchkonstruksi yang Anda kenal dari bahasa lain (atau ke Python raise/ except). Jika Anda mengalami kondisi kesalahan dan Anda akan throwmemperbaikinya dalam bahasa lain, Anda harus raisemenggunakan Ruby.

  2. Ruby throw/ catchmemungkinkan Anda menghentikan eksekusi dan memanjat tumpukan mencari catch(seperti raise/ rescuetidak), tetapi tidak dimaksudkan untuk kondisi kesalahan. Ini harus jarang digunakan, dan apakah ada hanya ketika "berjalan tumpukan sampai Anda menemukan yang sesuai catch" perilaku masuk akal untuk algoritma yang Anda tulis tetapi tidak masuk akal untuk memikirkan yang throwsesuai dengan kesalahan kondisi.

    Apa yang digunakan untuk menangkap dan melempar di Ruby? menawarkan beberapa saran tentang penggunaan throw/ catchkonstruksi yang bagus.

Perbedaan perilaku konkret di antara mereka termasuk:

  • rescue Fooakan menyelamatkan instance dari Footermasuk subclass dari Foo. catch(foo)hanya akan menangkap objek yang samaFoo ,. Anda tidak hanya tidak dapat memberikan catchnama kelas untuk menangkap contohnya, tetapi bahkan tidak akan melakukan perbandingan kesetaraan. Misalnya

    catch("foo") do
      throw "foo"
    end
    

    akan memberi Anda UncaughtThrowError: uncaught throw "foo"(atau ArgumentErrorversi Ruby sebelum 2.2)

  • Beberapa klausul penyelamatan dapat dicantumkan ...

    begin
      do_something_error_prone
    rescue AParticularKindOfError
      # Insert heroism here.
    rescue
      write_to_error_log
      raise
    end
    

    sementara beberapa catches perlu disarangkan ...

    catch :foo do
      catch :bar do
        do_something_that_can_throw_foo_or_bar
      end
    end
    
  • Telanjang rescuesetara dengan rescue StandardErrordan merupakan konstruksi idiomatis. "Telanjang catch", seperti catch() {throw :foo}, tidak akan pernah menangkap apa pun dan tidak boleh digunakan.

Mark Amery
sumber
Penjelasan yang baik tetapi menimbulkan pertanyaan, mengapa mereka merancang kenaikan gaji dalam ruby ​​= melempar dalam bahasa lain. dan kemudian juga termasuk melempar tetapi! = melempar dalam bahasa lain. Saya tidak dapat melihat logika asli mereka di sana
wired00
@ wired00 (Shrug.) Saya setuju bahwa tampaknya cukup eksentrik dibandingkan dengan bahasa populer lainnya saat ini.
Mark Amery
2
@ wired00: Ini telah disebut "meningkatkan" pengecualian sejak percobaan pertama dengan penanganan kesalahan terstruktur pada 1960-an, itu disebut "meningkatkan" pengecualian dalam artikel mani yang menemukan bentuk modern dari penanganan pengecualian, disebut "meningkatkan" pengecualian dalam Lisps dan Smalltalks, yang merupakan beberapa inspirasi utama untuk Ruby, dan itu disebut "meningkatkan" pengecualian atau "meningkatkan" interupsi dalam perangkat keras, di mana konsep itu ada bahkan sebelum konsep pemrograman " bahasa "ada Pertanyaannya seharusnya adalah: mengapa bahasa-bahasa lain itu mengubahnya?
Jörg W Mittag
@MarkAmery: Ingatlah bahwa banyak dari "bahasa populer lainnya" itu lebih muda dari Ruby atau setidaknya kontemporer. Jadi, pertanyaannya seharusnya: mengapa bahasa-bahasa lain itu tidak mengikuti Ruby (dan Smalltalk dan Lisp dan perangkat keras dan literatur).
Jörg W Mittag
@ JörgWMittag Menarik - Anda mengilhami saya untuk melakukan sedikit riset sejarah. C ++ memiliki gagasan "melempar" pengecualian tahun sebelum Ruby datang, dan per english.stackexchange.com/a/449209/73974 istilah tersebut sebenarnya kembali ke tahun 70-an ... jadi saya pikir kita masih bisa mengkritik Ruby untuk mengambil terminologi yang sudah ada dan menggunakannya untuk berarti sesuatu yang sama sekali berbeda.
Mark Amery