异常
在Nim中异常是对象。按照惯例,异常类型带有'Error'后缀。系统模型定义了一种异常层次结构,你要遵守。异常源自system.Exception,它提供公共的接口。
异常必须在堆上分配,因为它们的生存时间不确定。编译器将阻止你在栈空间创建异常。所有引发的异常应该在msg域说明原因。
一个约定是异常要在特殊的情况下才能被引发:例如,如果一个文件不能打开,这不应该引发一个异常因为这是很常见的(文件可能不存在)
raise语句
使用raise语句引发一个异常:
var
e: ref OSError
new(e)
e.msg = "the request to the OS failed"
raise e
如果raise关键字没有在一个异常后面,最后的异常将会被引发。为了达到避免重复公共代码模式的目的,可以使用系统模块中的模板newException:
raise newException(OSError, "the request to the OS failed")
try语句
try语句处理异常:
# read the first two lines of a text file that should contain numbers
# and tries to add them 读取一个文本文件的前两行,它应该包含数字已经尝试添加它们
var
f: File
if open(f, "numbers.txt"):
try:
let a = readLine(f)
let b = readLine(f)
echo "sum: ", parseInt(a) + parseInt(b)
except OverflowError:
echo "overflow!"
except ValueError:
echo "could not convert string to integer"
except IOError:
echo "IO error!"
except:
echo "Unknown exception!"
# reraise the unknown exception:
raise
finally:
close(f)
除非引发一个异常,try之后的语句才会执行。然后会执行except相应的部分。
如果这有一个异常没有明确的列出,将会执行空的except部分。它类似于if语句中的else部分。
如果存在finally部分,在异常处理之后它必须执行。
异常是在except部分处理,如果一个异常没有处理,它是通过调用堆栈传播。那意味着通常剩下的程序-不在一个finally的部分--是不执行的(如果异常发生)。
如果你需要访问实际的异常对象或者except分支中的消息,可以使用系统模型中的getCurrentException()和getCurrentExceptionMsg()的方法。例如:
try:
doSomethingHere()
except:
let
e = getCurrentException()
msg = getCurrentExceptionMsg()
echo "Got exception ", repr(e), " with message ", msg
注释过程提出异常
尽管使用可选项{.raises.}编译注释你可以确定一个过程是为了引发一组特定的异常,或者根本什么都没有。如果使用{.raises.}编译注释,编译器会验证这是正确的。例如:如果你指定一个过程引发IOError,在某些地方它(或者是它调用的方法之一)开始引发一个新的异常,编译器将会阻止过程编译。使用示例:
proc complexProc() {.raises: [IOError, ArithmeticError].} =
...
proc simpleProc() {.raises: [].} =
...
一旦你在程序中有这样的代码,如果引发异常的列表发生改变,编译器将会停止,在过程特定的地方出现一个错误,过程将停止验证语用以及不能捕获引发的异常,随着文件以及行未捕获的异常被引发,它可能帮助你找到异常改变的问题代码。
如果那你想为现有的代码添加{.raises.}编译注释,编译器也会帮组你。你可以添加{.effects.}编译注释语句都你的过程中,编译器将会输出所有已经推断的影响达到那一点(异常轨迹是nim的影响系统的一部分)。
另一种间接地方法通过一个过程找到引发的异常列表是使用nim doc2命令,它为了整个模块生成文档以及用引发异常列表装饰所有的过程。