ข้อแตกต่างระหว่าง Raising Exceptions กับ Throwing Exceptions ใน Ruby คืออะไร?


168

Ruby มีกลไกการยกเว้นสองแบบที่แตกต่างกัน: Throw / Catch และ Raise / Rescue

ทำไมเราถึงมีสอง

คุณควรใช้อันใดอันหนึ่งและไม่ใช่อันอื่น


“ การออกไปจากลูปซ้อนกัน” เป็นความต้องการทั่วไปในภาษาการเขียนโปรแกรมจำนวนมาก นอกจากนี้gotoในC / C ++เป็น @docwhat ได้กล่าวถึง, Java ได้มีป้ายกำกับแบ่งและดำเนินการต่อ (Python มีข้อเสนอที่ถูกปฏิเสธสำหรับเรื่องนี้)
Franklin Yu

คำตอบ:


104

ฉันคิดว่าhttp://hasno.info/ruby-gotchas-and-caveatsมีคำอธิบายที่ดีเกี่ยวกับความแตกต่าง:

การจับ / โยนนั้นไม่เหมือนกับการเพิ่ม / ช่วยเหลือ catch / throw ช่วยให้คุณออกจากบล็อกได้อย่างรวดเร็วกลับไปยังจุดที่กำหนด catch สำหรับสัญลักษณ์เฉพาะการช่วยยกคือข้อยกเว้นการจัดการสิ่งของที่เกี่ยวข้องกับวัตถุ Exception


1
อยากรู้ว่า ... การอ่านสิ่งนี้จาก iPad ดังนั้นจึงไม่สามารถทดสอบพวกเขาใน 1.9 แต่ gotchas เหล่านั้นบางส่วนไม่สามารถใช้งานได้ในรุ่นทับทิมล่าสุดใช่ไหม
เดนิสเดอเบอร์นาดี

12
ควรค่าแก่การรับรู้: raiseมีราคาแพงมาก throwไม่ใช่. คิดว่าthrowใช้gotoเพื่อออกจากวง
docwhat

4
@Denis คุณหมายถึง gotchas ไหน?
docwhat

1
ลิงค์เสีย!
morhook

ดูruby catch-throw และประสิทธิภาพเพื่อทำความเข้าใจเพิ่มเติมเกี่ยวกับความแตกต่างของประสิทธิภาพ
Franklin Yu

110
  • raise, fail, rescueและensureจับข้อผิดพลาดที่เรียกว่าเป็นข้อยกเว้น
  • throwและcatchมีการควบคุมการไหล

ไม่เหมือนกับภาษาอื่น ๆ การโยนและจับของ Ruby ไม่ได้ใช้สำหรับการยกเว้น แต่พวกเขามีวิธีที่จะยุติการดำเนินการก่อนเมื่อไม่จำเป็นต้องทำงานเพิ่มเติม (Grimm, 2011)

ยกเลิกระดับเดียวของการควบคุมการไหลเช่นห่วงสามารถทำได้ด้วยความเรียบง่ายwhile ยกเลิกหลายระดับของการควบคุมการไหลเช่นวงซ้อนกันสามารถทำได้ด้วยreturnthrow

ในขณะที่กลไกการยกเว้นของการยกและช่วยเหลือดีเยี่ยมสำหรับการละทิ้งการปฏิบัติเมื่อสิ่งต่าง ๆ ผิดพลาดบางครั้งก็ดีที่สามารถกระโดดออกจากโครงสร้างที่ซ้อนกันอย่างลึกล้ำระหว่างการประมวลผลปกติ นี่คือที่จับและโยนเข้ามามีประโยชน์ (โทมัสและล่า, 2001)

อ้างอิง

  1. กริมม์, Avdi "โยนจับเพิ่มกู้ภัย ... ฉันสับสนมาก!" บล็อก RubyLearning Np, 11 July 2011. เว็บ 1 มกราคม 2012 http://rubylearning.com/blog/2011/07/12/throw-catch-raise-rescue--im-so-confused/
  2. โทมัสเดฟและแอนดรูว์ฮันท์ "การเขียนโปรแกรม Ruby" : คู่มือสำหรับนักปฏิบัติในทางปฏิบัติ Np, 2001. เว็บ 29 กันยายน 2015 http://ruby-doc.com/docs/ProgrammingRuby/html/tut_exceptions.html

2
Avdi ดูไม่เหมือนที่เขาฟังในพ็อดแคสต์
hrdwdmrbl

2
ดูเหมือนว่าลิงก์ Ruby Learning จะไม่ทำงาน นี่เป็นอีกบล็อกโพสต์ที่กล่าวถึงความแตกต่าง: danielchangnyc.github.io/blog/2013/10/23/throw-raise
Dennis

ตลก rubylearning.com คิดว่าบทความของ Avdi ยังคงมี ฉันเดาว่านั่นเป็นสาเหตุที่เราคัดลอกเนื้อหาไปที่ SO ดังนั้นมันจะไม่สูญหาย!
Jared Beck

21

https://coderwall.com/p/lhkkug/don-t-confuse-ruby-s-throw-statement-with-raiseเสนอคำอธิบายที่ยอดเยี่ยมที่ฉันสงสัยว่าฉันสามารถปรับปรุงได้ ในการสรุปให้ดักจับตัวอย่างโค้ดบางส่วนจากโพสต์บล็อกเมื่อฉันไป:

  1. raise/ rescueเป็นแอนะล็อกที่ใกล้เคียงที่สุดกับthrow/ สิ่งcatchก่อสร้างที่คุณคุ้นเคยจากภาษาอื่น (หรือถึง Python raise/ except) หากคุณพบเงื่อนไขข้อผิดพลาดและคุณจะทำthrowมันในภาษาอื่นคุณควรraiseอยู่ใน Ruby

  2. Ruby's throw/ catchให้คุณหยุดการทำงานและไต่ขึ้นไปหา stack catch(เช่นraise/ rescueทำ) แต่ไม่ได้มีไว้สำหรับเงื่อนไขข้อผิดพลาดจริงๆ มันควรจะใช้ไม่บ่อยนักและจะเกิดขึ้นเมื่อcatchพฤติกรรม"เดินขึ้นไปบนสแต็กจนกว่าคุณจะพบพฤติกรรม" ที่เหมาะสมสำหรับอัลกอริทึมที่คุณกำลังเขียน แต่มันก็ไม่สมเหตุสมผลที่จะคิดว่าthrowสอดคล้องกับข้อผิดพลาด เงื่อนไข.

    อะไรคือการจับและโยนที่ใช้ในทับทิม? เสนอคำแนะนำบางอย่างเกี่ยวกับการใช้throw/ catchสร้าง

ความแตกต่างของพฤติกรรมที่เป็นรูปธรรมระหว่างพวกเขารวมถึง:

  • rescue Fooจะช่วยเหลือกรณีของการรวมถึงคลาสย่อยFoo จะจับเท่านั้นFoocatch(foo)วัตถุเดียวกันFooเท่านั้น. ไม่เพียง แต่คุณจะไม่สามารถส่งcatchชื่อคลาสเพื่อจับอินสแตนซ์ของมันได้ แต่จะไม่เปรียบเทียบความเท่าเทียมกัน ตัวอย่างเช่น

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

    จะให้ UncaughtThrowError: uncaught throw "foo" (หรือArgumentErrorรุ่น Ruby ก่อน 2.2)

  • สามารถแสดงรายการคำสั่งกู้ภัยหลายรายการ ...

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

    ในขณะที่หลายcatches จะต้องซ้อนกัน ...

    catch :foo do
      catch :bar do
        do_something_that_can_throw_foo_or_bar
      end
    end
  • เปลือยrescueเท่ากับrescue StandardErrorและเป็นสำนวนสร้าง เช่น "เปลือยcatch" catch() {throw :foo}จะไม่จับอะไรเลยและไม่ควรใช้


คำอธิบายที่ดี แต่ทำให้เกิดคำถามว่าทำไมบนโลกพวกเขาถึงออกแบบยกระดับเป็นทับทิม = ใช้ภาษาอื่น แล้วยังรวมถึงการโยน แต่มัน! = โยนในภาษาอื่น ๆ ฉันไม่เห็นตรรกะดั้งเดิมของพวกเขาที่นั่น
wired00

@ wired00 (ยัก)ฉันยอมรับว่ามันดูผิดปกติเมื่อเทียบกับภาษายอดนิยมอื่น ๆ ในวันนี้
Mark Amery

2
@ wired00: มันถูกเรียกว่า "การเพิ่ม" เป็นข้อยกเว้นนับตั้งแต่การทดลองครั้งแรกกับการจัดการข้อผิดพลาดอย่างเป็นระบบในปี 1960 มันถูกเรียกว่า "การเพิ่ม" ข้อยกเว้นในบทความน้ำเชื้อที่คิดค้นรูปแบบการจัดการข้อยกเว้นที่ทันสมัย "การเพิ่ม" ข้อยกเว้นใน Lisps และ Smalltalks ซึ่งเป็นแรงบันดาลใจหลักสำหรับ Ruby และเรียกว่า "การเพิ่ม" ข้อยกเว้นหรือ "การเพิ่ม" การขัดจังหวะในฮาร์ดแวร์ซึ่งแนวคิดนี้มีอยู่ก่อนแนวคิดของการเขียนโปรแกรม " ภาษา "มีอยู่จริง คำถามควรจะเป็น: ทำไมภาษาอื่น ๆ เหล่านั้นเปลี่ยนที่?
Jörg W Mittag

@MarkAmery: โปรดจำไว้ว่า "ภาษายอดนิยมอื่น ๆ " เหล่านั้นอายุน้อยกว่าทับทิมหรืออย่างน้อยก็ในปัจจุบัน ดังนั้นคำถามที่ควรจะเป็น: ทำไมภาษาเหล่านั้นไม่ได้ทำตามทับทิม (และ Smalltalk และ Lisp และฮาร์ดแวร์และวรรณกรรม)
Jörg W Mittag

@ JörgWMittagที่น่าสนใจ - คุณเป็นแรงบันดาลใจให้ฉันทำวิจัยทางประวัติศาสตร์เล็กน้อย C ++ มีแนวคิดเกี่ยวกับ "การขว้างปา" หนึ่งปีก่อนที่ Ruby จะมาพร้อมและต่อenglish.stackexchange.com/a/449209/73974คำนั้นกลับไปสู่ยุค 70 จริง ๆ ... ดังนั้นฉันคิดว่าเรายังคงวิจารณ์ Ruby สำหรับ การสร้างคำศัพท์และใช้มันเพื่อหมายถึงบางสิ่งที่แตกต่างอย่างสิ้นเชิง
Mark Amery
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.