Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> Ruby

Hiểu Ruby: Exception vs StandardError – Khác biệt then chốt

"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