Một ngoại lệ (exception) được raise có thể bị rescue (bắt) để ngăn nó crash ứng dụng khi đạt đến đỉnh call stack. Trong Ruby, ta dùng từ khóa rescue cho việc này.
Khi rescue exception trong Ruby, bạn có thể chỉ định class lỗi cụ thể cần bắt.
begin raise 'This exception will be rescued!' rescue StandardError => e puts "Rescued: #{e.inspect}" endLưu ý: Khi dùng raise không chỉ định exception class, Ruby mặc định dùng RuntimeError.
Ngoài việc chỉ định một exception class, bạn có thể truyền mảng các exception class cho rescue. Điều này cho phép xử lý nhiều lỗi cùng cách.
begin raise 'This exception will be rescued!' rescue StandardError, AnotherError => e puts "Rescued: #{e.inspect}" endNhiều block rescue có thể dùng để xử lý lỗi khác nhau theo cách khác nhau. Hữu ích khi làm việc với library tạo ra exception khác nhau cho các scenario khác nhau.
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}" endHệ Thống Phân Cấp Ngoại Lệ
Hệ thống phân cấp exception của Ruby dùng để phân biệt loại lỗi khác nhau, đồng thời cho phép rescue từ một nhóm lỗi mà không cần liệt kê tất cả.
Mặc dù library có thể định nghĩa subclass exception riêng, danh sách built-in exception subclass trên Ruby 2.5 như sau:
- NoMemoryError - ScriptError - LoadError - NotImplementedError - SyntaxError - SecurityError - SignalException - Interrupt - StandardError (mặc định cho `rescue`) - ArgumentError - UncaughtThrowError - EncodingError - FiberError - IOError - EOFError - IndexError - KeyError - StopIteration - LocalJumpError - NameError - NoMethodError - RangeError - FloatDomainError - RegexpError - RuntimeError (mặc định cho `raise`) - SystemCallError - Errno::* - ThreadError - TypeError - ZeroDivisionError - SystemExit - SystemStackError - fatal (không thể rescue) Khi bỏ qua exception class trong block rescue, StandardError được giả định. Vì ArgumentError và NoMethodError là subclass của StandardError, chúng bị rescue khi xảy ra trong block.
Ví dụ tốt về cách hierarchy exception hoạt động là SystemCallError, một exception class thấp cấp, platform-dependent. Thường gặp khi đọc/ghi file.
Phương thức File.read của Ruby sẽ raise exception nếu không đọc được file. Có thể xảy ra vì nhiều lý do, như file không tồn tại hoặc chương trình không có quyền đọc.
Vì những vấn đề này platform-dependent, Ruby có thể raise exception khác nhau tùy OS. Cho lỗi thấp cấp này, Ruby implement danh sách Errno::*-exception khác nhau cho mỗi platform.
Tất cả Errno::* exception này là subclass của SystemCallError. Mặc dù platform-specific, chúng vẫn dùng được trong block rescue bằng cách rescue từ SystemCallError.
begin File.read("does/not/exist") rescue SystemCallError => e puts "Rescued: #{e.inspect}" endNuốt Chửng Exception (Swallowing Exceptions)
Thường thì nên cụ thể nhất khi rescue exception, để tránh vô tình nuốt chửng exception.
image = nil begin File.read(image.filename) rescue puts "File can't be read!" endTrong ví dụ này, biến image là nil, nên raise NoMethodError khi gọi #filename lên nó (NoMethodError: undefined method `filename' for nil:NilClass). Vì mọi subclass StandardError đều bị rescue (kể cả NoMethodError), exception bị nuốt và in ra "File can't be read!". Điều này che giấu bug tiềm ẩn trong code.
Lưu ý: Mặc dù có thể, dùng superclass Exception trong block rescue rất không khuyến khích.
Có câu hỏi về raise hoặc rescue exception trong Ruby? Đừng ngần ngại liên hệ @AppSignal. Chúng tôi rất muốn biết bạn thấy bài này như thế nào, hoặc chủ đề gì bạn muốn biết thêm.