"Không bao giờ rescue Exception trong Ruby!"
Có thể bạn đã nghe. Lời khuyên tốt, nhưng hơi confusing trừ khi đã "trong cuộc". Hãy phân tích câu này xem ý nghĩa gì.
Bạn biết trong Ruby, rescue exception như sau:
begin
do_something()
rescue => e
puts e # e là exception object chứa info lỗi.
end
Và rescue specific error bằng cách cung cấp classname:
begin
do_something()
rescue ActiveRecord::RecordNotFound => e
puts e # Chỉ rescue RecordNotFound, hoặc class kế thừa từ RecordNotFound
end
Mọi loại exception trong Ruby chỉ là class. Trong ví dụ, `ActiveRecord::RecordNotFound` chỉ là tên class tuân theo convention.
Quan trọng: khi rescue `RecordNotFound`, bạn cũng rescue exception kế thừa từ nó.
Tại sao không nên rescue Exception
Vấn đề rescue `Exception`: nó rescue mọi exception kế thừa từ `Exception`. Tức là... tất cả!
Đó là vấn đề vì có exception dùng nội bộ bởi Ruby. Không liên quan app bạn, nuốt chửng chúng gây điều xấu.
Vài "lớn" nhất:
- SignalException::Interrupt — Rescue cái này, không thoát app bằng Ctrl-C.
- ScriptError::SyntaxError — Nuốt syntax error → `puts("Forgot something)` fail lặng lẽ.
- NoMemoryError — Muốn biết chuyện gì khi chương trình chạy tiếp sau khi hết RAM? Tôi cũng không.
begin
do_something()
rescue Exception => e
# Đừng làm thế. Sẽ nuốt mọi exception. Không gì lọt qua.
end
Tôi đoán bạn không muốn nuốt system-level exception. Chỉ muốn bắt tất cả application-level error — exception do code của bạn gây ra.
May mắn có cách dễ dàng.
Rescue StandardError thay vì
Tất cả exception bạn nên quan tâm đều kế thừa từ `StandardError`. Những "người bạn cũ":
- NoMethodError — raise khi gọi method không tồn tại
- TypeError — do kiểu `1 + ""`
- RuntimeError — ai quên được RuntimeError?
Để rescue lỗi này, rescue `StandardError`. CÓ THỂ viết:
begin
do_something()
rescue StandardError => e
# Chỉ exception app bị nuốt. SyntaxError v.v. để yên.
end
Nhưng Ruby làm dễ hơn.
Khi không chỉ định exception class, Ruby giả định bạn muốn `StandardError`. Code dưới giống hệt code trên:
begin
do_something()
rescue => e
# Tương đương rescue StandardError
end
Custom Exception nên kế thừa StandardError
Ý nghĩa gì nếu bạn tạo custom exception?
Nên luôn kế thừa `StandardError`, KHÔNG BAO GIỜ từ `Exception`. Kế thừa Exception xấu vì phá vỡ behavior kỳ vọng của rescue. Người ta tưởng rescue hết app-level error mà exception của bạn lướt qua.
class SomethingBad < StandardError; end
raise SomethingBad
Cây Exception
Vì exception Ruby implement qua class hierarchy, thấy rõ khi layout. Dưới là list exception class ship với Ruby standard library. Gem third-party như Rails thêm class khác, nhưng đều kế thừa từ class nào đó trong list này.
Exception
NoMemoryError
ScriptError
LoadError
NotImplementedError
SyntaxError
SignalException
Interrupt
StandardError
ArgumentError
IOError
EOFError
IndexError
LocalJumpError
NameError
NoMethodError
RangeError
FloatDomainError
RegexpError
RuntimeError
SecurityError
SystemCallError
SystemStackError
ThreadError
TypeError
ZeroDivisionError
SystemExit
fatal