class Exception

Exception 类及其子类用于指示发生了错误或其他问题,可能需要处理。请参阅 异常

一个 Exception 对象携带某些信息

内置异常类层次结构

Exception 类的内置子类的层次结构

公共类方法

exception(message = nil) → self or new_exception

返回与 self 相同类的异常对象;对于创建相似的异常但具有不同的消息很有用。

messagenil 时,返回 self

x0 = StandardError.new('Boom') # => #<StandardError: Boom>
x1 = x0.exception              # => #<StandardError: Boom>
x0.__id__ == x1.__id__         # => true

message可转换为字符串的对象(即使与原始消息相同)时,返回一个新的异常对象,该对象的类与 self 相同,并且其消息是给定的 message

x1 = x0.exception('Boom') # => #<StandardError: Boom>
x0..equal?(x1)            # => false
new(message = nil) → exception 点击以切换源代码

返回一个新的异常对象。

给定的 message 应该是可转换为字符串的对象;请参阅方法 message;如果未给出,则消息是新实例的类名(可能是子类的名称)

示例

Exception.new         # => #<Exception: Exception>
LoadError.new         # => #<LoadError: LoadError> # Subclass of Exception.
Exception.new('Boom') # => #<Exception: Boom>
static VALUE
exc_initialize(int argc, VALUE *argv, VALUE exc)
{
    VALUE arg;

    arg = (!rb_check_arity(argc, 0, 1) ? Qnil : argv[0]);
    return exc_init(exc, arg);
}
to_tty? → true or false 点击以切换源代码

如果异常消息将发送到终端设备,则返回 true

static VALUE
exc_s_to_tty_p(VALUE self)
{
    return RBOOL(rb_stderr_tty_p());
}

公共实例方法

self == object → true or false 点击以切换源代码

返回 object 是否与 self 的类相同,并且其 messagebacktrace 是否与 self 的相等。

static VALUE
exc_equal(VALUE exc, VALUE obj)
{
    VALUE mesg, backtrace;

    if (exc == obj) return Qtrue;

    if (rb_obj_class(exc) != rb_obj_class(obj)) {
        int state;

        obj = rb_protect(try_convert_to_exception, obj, &state);
        if (state || UNDEF_P(obj)) {
            rb_set_errinfo(Qnil);
            return Qfalse;
        }
        if (rb_obj_class(exc) != rb_obj_class(obj)) return Qfalse;
        mesg = rb_check_funcall(obj, id_message, 0, 0);
        if (UNDEF_P(mesg)) return Qfalse;
        backtrace = rb_check_funcall(obj, id_backtrace, 0, 0);
        if (UNDEF_P(backtrace)) return Qfalse;
    }
    else {
        mesg = rb_attr_get(obj, id_mesg);
        backtrace = exc_backtrace(obj);
    }

    if (!rb_equal(rb_attr_get(exc, id_mesg), mesg))
        return Qfalse;
    return rb_equal(exc_backtrace(exc), backtrace);
}
backtrace → array or nil 点击以切换源代码

将回溯(导致异常的代码位置列表)作为字符串数组返回。

示例(假设代码存储在名为 t.rb 的文件中)

def division(numerator, denominator)
  numerator / denominator
end

begin
  division(1, 0)
rescue => ex
  p ex.backtrace
  # ["t.rb:2:in 'Integer#/'", "t.rb:2:in 'Object#division'", "t.rb:6:in '<main>'"]
  loc = ex.backtrace.first
  p loc.class
  # String
end

当引发(请参阅 Kernel#raise)或在 set_backtrace 的中间处理过程中,此方法返回的值可能会被调整。

另请参阅 backtrace_locations,它以结构化对象的形式提供相同的值。(请注意,当手动调整回溯时,两个值可能不一致。)

请参阅回溯

static VALUE
exc_backtrace(VALUE exc)
{
    VALUE obj;

    obj = rb_attr_get(exc, id_bt);

    if (rb_backtrace_p(obj)) {
        obj = rb_backtrace_to_str_ary(obj);
        /* rb_ivar_set(exc, id_bt, obj); */
    }

    return obj;
}
backtrace_locations → array or nil 点击以切换源代码

将回溯(导致异常的代码位置列表)作为 Thread::Backtrace::Location 实例的数组返回。

示例(假设代码存储在名为 t.rb 的文件中)

def division(numerator, denominator)
  numerator / denominator
end

begin
  division(1, 0)
rescue => ex
  p ex.backtrace_locations
  # ["t.rb:2:in 'Integer#/'", "t.rb:2:in 'Object#division'", "t.rb:6:in '<main>'"]
  loc = ex.backtrace_locations.first
  p loc.class
  # Thread::Backtrace::Location
  p loc.path
  # "t.rb"
  p loc.lineno
  # 2
  p loc.label
  # "Integer#/"
end

当引发(请参阅 Kernel#raise)或在 set_backtrace 的中间处理过程中,此方法返回的值可能会被调整。

另请参阅 backtrace,它以字符串数组的形式提供相同的值。(请注意,当手动调整回溯时,两个值可能不一致。)

请参阅 回溯

static VALUE
exc_backtrace_locations(VALUE exc)
{
    VALUE obj;

    obj = rb_attr_get(exc, id_bt_locations);
    if (!NIL_P(obj)) {
        obj = rb_backtrace_to_location_ary(obj);
    }
    return obj;
}
cause → exception or nil 点击以切换源代码

返回全局变量 $! 的先前值,该值可能为 nil (请参阅 全局变量

begin
  raise('Boom 0')
rescue => x0
  puts "Exception: #{x0};  $!: #{$!};  cause: #{x0.cause.inspect}."
  begin
    raise('Boom 1')
  rescue => x1
    puts "Exception: #{x1};  $!: #{$!};  cause: #{x1.cause}."
    begin
      raise('Boom 2')
    rescue => x2
      puts "Exception: #{x2};  $!: #{$!};  cause: #{x2.cause}."
    end
  end
end

输出

Exception: Boom 0;  $!: Boom 0;  cause: nil.
Exception: Boom 1;  $!: Boom 1;  cause: Boom 0.
Exception: Boom 2;  $!: Boom 2;  cause: Boom 1.
static VALUE
exc_cause(VALUE exc)
{
    return rb_attr_get(exc, id_cause);
}
detailed_message(highlight: false, **kwargs) → string 点击以切换源代码

返回带有增强功能的消息字符串

  • 在第一行中包含异常类名。

  • 如果关键字 highlight 的值为 true,则包括粗体和下划线 ANSI 代码(见下文)以增强消息的外观。

示例

begin
  1 / 0
rescue => x
  p x.message
  p x.detailed_message                  # Class name added.
  p x.detailed_message(highlight: true) # Class name, bolding, and underlining added.
end

输出

"divided by 0"
"divided by 0 (ZeroDivisionError)"
"\e[1mdivided by 0 (\e[1;4mZeroDivisionError\e[m\e[1m)\e[m"

此方法被 Ruby 标准库中的一些 gem 重写以添加信息

  • DidYouMean::Correctable#detailed_message。

  • ErrorHighlight::CoreExt#detailed_message。

  • SyntaxSuggest#detailed_message。

重写方法必须容忍传递的关键字参数,其中可能包括(但不限于)

  • :highlight.

  • :did_you_mean.

  • :error_highlight.

  • :syntax_suggest.

重写方法还应谨慎使用 ANSI 代码增强功能;请参阅 消息

static VALUE
exc_detailed_message(int argc, VALUE *argv, VALUE exc)
{
    VALUE opt;

    rb_scan_args(argc, argv, "0:", &opt);

    VALUE highlight = check_highlight_keyword(opt, 0);

    extern VALUE rb_decorate_message(const VALUE eclass, VALUE emesg, int highlight);

    return rb_decorate_message(CLASS_OF(exc), rb_get_message(exc), RTEST(highlight));
}
exception(message = nil) → self or new_exception 点击以切换源代码

返回与 self 相同类的异常对象;对于创建相似的异常但具有不同的消息很有用。

messagenil 时,返回 self

x0 = StandardError.new('Boom') # => #<StandardError: Boom>
x1 = x0.exception              # => #<StandardError: Boom>
x0.__id__ == x1.__id__         # => true

message可转换为字符串的对象(即使与原始消息相同)时,返回一个新的异常对象,该对象的类与 self 相同,并且其消息是给定的 message

x1 = x0.exception('Boom') # => #<StandardError: Boom>
x0..equal?(x1)            # => false
static VALUE
exc_exception(int argc, VALUE *argv, VALUE self)
{
    VALUE exc;

    argc = rb_check_arity(argc, 0, 1);
    if (argc == 0) return self;
    if (argc == 1 && self == argv[0]) return self;
    exc = rb_obj_clone(self);
    rb_ivar_set(exc, id_mesg, argv[0]);
    return exc;
}
full_message(highlight: true, order: :top) → string 点击以切换源代码

返回增强的消息字符串

  • 包括异常类名。

  • 如果关键字 highlight 的值为 true(不是 nilfalse),则包括粗体 ANSI 代码(见下文)以增强消息的外观。

  • 包括回溯

    • 如果关键字 order 的值为 :top (默认值),则首先列出错误消息和最内层的回溯条目。

    • 如果关键字 order 的值为 :bottom,则最后列出错误消息和最内层的条目。

示例

def baz
  begin
    1 / 0
  rescue => x
    pp x.message
    pp x.full_message(highlight: false).split("\n")
    pp x.full_message.split("\n")
  end
end
def bar; baz; end
def foo; bar; end
foo

输出

"divided by 0"
["t.rb:3:in 'Integer#/': divided by 0 (ZeroDivisionError)",
 "\tfrom t.rb:3:in 'Object#baz'",
 "\tfrom t.rb:10:in 'Object#bar'",
 "\tfrom t.rb:11:in 'Object#foo'",
 "\tfrom t.rb:12:in '<main>'"]
["t.rb:3:in 'Integer#/': \e[1mdivided by 0 (\e[1;4mZeroDivisionError\e[m\e[1m)\e[m",
 "\tfrom t.rb:3:in 'Object#baz'",
 "\tfrom t.rb:10:in 'Object#bar'",
 "\tfrom t.rb:11:in 'Object#foo'",
 "\tfrom t.rb:12:in '<main>'"]

重写方法应谨慎使用 ANSI 代码增强功能;请参阅 消息

static VALUE
exc_full_message(int argc, VALUE *argv, VALUE exc)
{
    VALUE opt, str, emesg, errat;
    VALUE highlight, order;

    rb_scan_args(argc, argv, "0:", &opt);

    highlight = check_highlight_keyword(opt, 1);
    order = check_order_keyword(opt);

    {
        if (NIL_P(opt)) opt = rb_hash_new();
        rb_hash_aset(opt, sym_highlight, highlight);
    }

    str = rb_str_new2("");
    errat = rb_get_backtrace(exc);
    emesg = rb_get_detailed_message(exc, opt);

    rb_error_write(exc, emesg, errat, str, opt, highlight, order);
    return str;
}
inspect → string 点击以切换源代码

返回 self 的字符串表示形式

x = RuntimeError.new('Boom')
x.inspect # => "#<RuntimeError: Boom>"
x = RuntimeError.new
x.inspect # => "#<RuntimeError: RuntimeError>"
static VALUE
exc_inspect(VALUE exc)
{
    VALUE str, klass;

    klass = CLASS_OF(exc);
    exc = rb_obj_as_string(exc);
    if (RSTRING_LEN(exc) == 0) {
        return rb_class_name(klass);
    }

    str = rb_str_buf_new2("#<");
    klass = rb_class_name(klass);
    rb_str_buf_append(str, klass);

    if (RTEST(rb_str_include(exc, rb_str_new2("\n")))) {
        rb_str_catf(str, ":%+"PRIsVALUE, exc);
    }
    else {
        rb_str_buf_cat(str, ": ", 2);
        rb_str_buf_append(str, exc);
    }

    rb_str_buf_cat(str, ">", 1);

    return str;
}
message → string 点击以切换源代码

返回 to_s

请参阅 消息

static VALUE
exc_message(VALUE exc)
{
    return rb_funcallv(exc, idTo_s, 0, 0);
}
set_backtrace(value) → value 点击以切换源代码

设置 self 的回溯值;返回给定的 value

value 可能为

使用 Thread::Backtrace::Location 数组是最一致的选择:它同时设置了 backtracebacktrace_locations。如果可能,应优先使用。可以从 Kernel#caller_locations 获取合适的位置数组,从另一个错误复制,或者仅设置为当前错误的 backtrace_locations 的调整结果

require 'json'

def parse_payload(text)
  JSON.parse(text)  # test.rb, line 4
rescue JSON::ParserError => ex
  ex.set_backtrace(ex.backtrace_locations[2...])
  raise
end

parse_payload('{"wrong: "json"')
# test.rb:4:in 'Object#parse_payload': unexpected token at '{"wrong: "json"' (JSON::ParserError)
#
# An error points to the body of parse_payload method,
# hiding the parts of the backtrace related to the internals
# of the "json" library

# The error has both #backtace and #backtrace_locations set
# consistently:
begin
  parse_payload('{"wrong: "json"')
rescue => ex
  p ex.backtrace
  # ["test.rb:4:in 'Object#parse_payload'", "test.rb:20:in '<main>'"]
  p ex.backtrace_locations
  # ["test.rb:4:in 'Object#parse_payload'", "test.rb:20:in '<main>'"]
end

当所需的位置堆栈不可用且应从头开始构造时,可以使用字符串数组或单个字符串。在这种情况下,只有 backtrace 受影响

def parse_payload(text)
  JSON.parse(text)
rescue JSON::ParserError => ex
  ex.set_backtrace(["dsl.rb:34", "framework.rb:1"])
  # The error have the new value in #backtrace:
  p ex.backtrace
  # ["dsl.rb:34", "framework.rb:1"]

  # but the original one in #backtrace_locations
  p ex.backtrace_locations
  # [".../json/common.rb:221:in 'JSON::Ext::Parser.parse'", ...]
end

parse_payload('{"wrong: "json"')

使用 nil 调用 set_backtrace 会清除 backtrace,但不会影响 backtrace_locations

def parse_payload(text)
  JSON.parse(text)
rescue JSON::ParserError => ex
  ex.set_backtrace(nil)
  p ex.backtrace
  # nil
  p ex.backtrace_locations
  # [".../json/common.rb:221:in 'JSON::Ext::Parser.parse'", ...]
end

parse_payload('{"wrong: "json"')

在重新引发此类异常时,backtracebacktrace_locations 都将设置为重新引发的位置

def parse_payload(text)
  JSON.parse(text)
rescue JSON::ParserError => ex
  ex.set_backtrace(nil)
  raise # test.rb, line 7
end

begin
  parse_payload('{"wrong: "json"')
rescue => ex
  p ex.backtrace
  # ["test.rb:7:in 'Object#parse_payload'", "test.rb:11:in '<main>'"]
  p ex.backtrace_locations
  # ["test.rb:7:in 'Object#parse_payload'", "test.rb:11:in '<main>'"]
end

请参阅 回溯

static VALUE
exc_set_backtrace(VALUE exc, VALUE bt)
{
    VALUE btobj = rb_location_ary_to_backtrace(bt);
    if (RTEST(btobj)) {
        rb_ivar_set(exc, id_bt, btobj);
        rb_ivar_set(exc, id_bt_locations, btobj);
        return bt;
    }
    else {
        return rb_ivar_set(exc, id_bt, rb_check_backtrace(bt));
    }
}
to_s → string 点击以切换源代码

返回 self 的字符串表示形式

x = RuntimeError.new('Boom')
x.to_s # => "Boom"
x = RuntimeError.new
x.to_s # => "RuntimeError"
static VALUE
exc_to_s(VALUE exc)
{
    VALUE mesg = rb_attr_get(exc, idMesg);

    if (NIL_P(mesg)) return rb_class_name(CLASS_OF(exc));
    return rb_String(mesg);
}