除了 Ruby 基础教程 的翻译之外,好久没接触到 Ruby 语言的一些知识了。这次一时兴起,想讲讲 Ruby 的一些知识,找来找去,还是决定从 错误处理 ( error handling ) 开始。
本章节,我们将会涉及到一下几个知识点:
- Ruby 中的错误 ( error ) 是什么 ?
- 怎样抛出 ( throw ) 和 捕获 ( catch ) 一个错误。
- 错误和一些魔法变量。
Ruby 中的错误是什么?
这看起来简直就是一个非常简单的问题,简单到感觉都不用再来讲解一次 。 but ,真的是这样么 ?
众所周知, Ruby 是一个完全面向对象的语言。所以呢,哪怕是一个错误,也是某个类的实例,而且这个类必须有一个特殊的祖先类 Exception
( 不一定是父类 )。
例如,我们可以在 irb ( Ruby 的交互式解释命令行工具) 中输入以下内容来看看各种系统级错误是否是 Exception 的实例
irb> RuntimeError.ancestors.include? Exception => true irb> NoMethodError.ancestors.include? Exception => true
这也是为什么通常把 Ruby 中的错误称之为 「 异常 」( Exception ) 的原因。
由于错误也是一个类,因此,Ruby 提供了一堆实例方法来处理错误。这些方法都在 Exception 中定义,且可以被所有子类使用。
例如,Exception#backtrace
方法返回错误堆栈,而 Exceptio#message
方法则返回与错误关联的摘要消息。
如果你想了解 Exception 类的更多信息,目前只有去访问官方文档。我们网站,会在适当的时候 ( 主要看心情 ) 撰写一些文章来解释解释。
怎么抛出 ( throw ) 和捕获 ( catch ) 一个错误
别的语言有的功能,在 Ruby 中只会更多而不会更少,像捕获异常这种机制,我们的大 Ruby 也是有的,Kernel#raise
方法就用来负责在 Ruby 中引发 ( 抛出 ) 错误
irb> raise 'an error occurred' RuntimeError: an error occurred irb> raise NoMethodError, 'an error occurred' NoMethodError: an error occurred
上面的代码中,当第一次调用 Kernel#raise
方法时,可以看到该方法抛出了一个 运行时错误 ( RuntimeError
) ,这是默认错误类型。 因为我们并没有对传递其它的错误类型作为参数
相反,在第二次调用 Kernel#raise
时,引发的异常是 NoMethodError
- 因为我们将它定义为方法调用的第一个参数。
捕获错误
当然了,抛出了错误就要对错误进行处理,不然系统和应用会崩溃的,也就是停止运行。
Ruby 则提供了 begin ... end
中的 resuce
字句用于捕获 ( catch ) 错误
begin raise NoMethodError, 'an error occurred' rescue NoMethodError => e puts "#{e.class}: #{e.message}" end
运行结果为 NoMethodError: an error occurred
上面的代码中,rescue
子句捕获了在 begin ... end
块中调用 Kernel#raise
方法引发的NoMethodError
异常
begin ... end
块可以也通常用于根据每个代码块可能发生的错误划分代码块
begin 3 / 0 rescue ZeroDivisionError => e puts "#{e.class}: #{e.message}" end begin "my string".odd? rescue NoMethodError => e puts "#{e.class}: #{e.message}" end
上面的代码输出结果如下
ZeroDivisionError: divided by 0 NoMethodError: undefined method `odd?' for "my string":String
请注意,在第一个 begin ... end
块中,3/0
是一个除零错误,是通过 Ruby 中的 Integer#/
方法在内部抛出的一个 ZeroDivisionError
而第二个 begin ... end
块,因为调用了一个不存在的方法 BasicObject#method_missing
,所以 Ruby 抛出了 NoMethodError
错误。
错误和魔法变量
刚刚我们看到了如何使用 rescue
关键字来捕获异常。其实,除了参数 e
之外,Ruby 会给两个特殊的魔法变量赋值,方便我们在捕获错误时对错误进行处理。
$!
变量包含了抛出的异常实例$@
变量则包含了抛出错误时的堆栈实例
这两个变量的使用方法一般如下
begin "my string".odd? rescue NoMethodError => e puts "#{$!.class}: #{$!.message}" $@.each { |loc| puts loc } # SAME AS # puts "#{e.class}: #{e.message}" # e.backtrace.each { |loc| puts loc } end
运行结果如下
NoMethodError: undefined method `odd?' for "my string":String /workspace.rb:87:in `eval' /workspace.rb:87:in `evaluate' ... /bin/irb:11:in `<main>'
需要注意的是,$!
和 $@
两个变量只存在于 rescue
块中,超出了这个块就会被设置为 nil
结束语
好的知识点,文章不在于重复不重复,而是每次阅读的时候,都有新的感悟