Exception ที่ถูก raise สามารถ rescue ได้เพื่อป้องกันไม่ให้ crash application เมื่อมันถึงจุดสูงสุดของ call stack ใน Ruby เราใช้ keyword rescue สำหรับสิ่งนี้
เมื่อ rescue exception ใน Ruby คุณสามารถระบุ error class เฉพาะที่ควร rescue
begin
raise 'This exception will be rescued!'
rescue StandardError => e
puts "Rescued: #{e.inspect}"
end
หมายเหตุ: เมื่อใช้ raise โดยไม่ระบุ exception class Ruby จะ default เป็น RuntimeError
นอกจากระบุ exception class เดียวแล้ว คุณสามารถส่ง array ของ exception classes ให้ rescue keyword ได้ ซึ่งช่วยให้ตอบสนองหลาย errors ด้วยวิธีเดียวกัน
begin
raise 'This exception will be rescued!'
rescue StandardError, AnotherError => e
puts "Rescued: #{e.inspect}"
end
หลาย rescue blocks ใช้จัดการ errors ต่างๆ แบบต่างกันได้ มีประโยชน์เมื่อทำงานกับ library ที่ผลิต exceptions ต่างกันสำหรับสถานการณ์ต่างๆ
begin
raise 'This exception will be rescued!'
rescue StandardError => e
puts "Rescued: #{e.inspect}"
rescue AnotherError => e
puts "Rescued, but with a different block: #{e.inspect}"
end
ลำดับชั้นของ Exception
ลำดับชั้น exception ของ Ruby ใช้แยกประเภท errors ที่ต่างกัน พร้อมให้คุณ rescue จากกลุ่ม errors โดยไม่ต้องระบุทั้งหมด
แม้ libraries จะ define exception subclasses ของตัวเองได้ รายการ built-in exception subclasses บน Ruby 2.5 เหมือนนี้:
- NoMemoryError
- ScriptError
- LoadError
- NotImplementedError
- SyntaxError
- SecurityError
- SignalException
- Interrupt
- StandardError (default สำหรับ `rescue`)
- ArgumentError
- UncaughtThrowError
- EncodingError
- FiberError
- IOError
- EOFError
- IndexError
- KeyError
- StopIteration
- LocalJumpError
- NameError
- NoMethodError
- RangeError
- FloatDomainError
- RegexpError
- RuntimeError (default สำหรับ `raise`)
- SystemCallError
- Errno::*
- ThreadError
- TypeError
- ZeroDivisionError
- SystemExit
- SystemStackError
- fatal (rescue ไม่ได้)
เมื่อไม่ระบุ exception class ใน rescue block จะถือว่าเป็น StandardError เนื่องจาก ArgumentError และ NoMethodError เป็น subclasses ของ StandardError จึงถูก rescue เมื่อเกิดใน block
ตัวอย่างดีของลำดับชั้น exception คือ SystemCallError ซึ่งเป็น low-level platform-dependent exception class มักเห็นเมื่ออ่านหรือเขียนไฟล์
เมธอด File.read ของ Ruby จะ raise exception หากอ่านไฟล์ล้มเหลว สาเหตุหลายอย่าง เช่น ไฟล์ไม่มีอยู่ หรือโปรแกรมไม่มี permission อ่าน
เนื่องจากปัญหาเหล่านี้ platform-dependent Ruby อาจ raise exceptions ต่างกันขึ้นอยู่กับ OS ที่รัน สำหรับ low-level errors เหล่านี้ Ruby implement リスト Errno::*-exceptions แตกต่างสำหรับแต่ละ platform
ทั้งหมด Errno::* exceptions เป็น subclasses ของ SystemCallError 雖然เป็น platform-specific แต่ยังใช้ใน rescue block ได้โดย rescue จาก SystemCallError
begin
File.read("does/not/exist")
rescue SystemCallError => e
puts "Rescued: #{e.inspect}"
end
การกลืน Exceptions
ปกติควรระบุเฉพาะเจาะจงที่สุดเมื่อ rescue exceptions เพื่อป้องกันการกลืน exceptions โดยไม่ตั้งใจ
image = nil
begin
File.read(image.filename)
rescue
puts "File can't be read!"
end
ในตัวอย่างนี้ ตัวแปร image เป็น nil จึง raise NoMethodError เมื่อพยายามเรียก #filename บนมัน (NoMethodError: undefined method `filename' for nil:NilClass) เนื่องจากทุก StandardError subclass ถูก rescue (รวม NoMethodError) exception จึงถูกกลืนและพิมพ์ "File can't be read!" ซึ่งซ่อน bug ที่เป็นไปได้ในโค้ด
หมายเหตุ: 雖然เป็นไปได้ แต่การใช้ Exception superclass ใน rescue block ไม่ได้รับแนะนำอย่างยิ่ง
มีคำถามเกี่ยวกับการ raise หรือ rescue exceptions ใน Ruby? อย่าลังเลติดต่อเราที่ @AppSignal เรายินดีรู้ความคิดเห็นว่าชอบบทความนี้หรือไม่ หรือมีหัวข้ออื่นอยากรู้เพิ่มเติม