第一版是为 Lua 5.0 编写的。虽然在很大程度上仍然适用于后续版本,但也有一些区别。
第四版针对 Lua 5.3,可在 亚马逊 和其他书店购买。
购买本书,您还将帮助支持 Lua 项目


8.3 – 错误

Errare humanum est。因此,我们必须尽力处理错误。由于 Lua 是一种扩展语言,经常嵌入在应用程序中,因此在发生错误时,它不能简单地崩溃或退出。相反,每当发生错误时,Lua 都会结束当前块并返回到应用程序。

Lua 遇到的任何意外情况都会引发错误。当您(即您的程序)尝试添加不是数字的值、调用不是函数的值、索引不是表的表等时,就会发生错误。(您可以使用元表修改此行为,我们稍后会看到。)您还可以显式引发错误,调用error 函数;其参数是错误消息。通常,该函数是处理代码中错误的适当方式

    print "enter a number:"
    n = io.read("*number")
    if not n then error("invalid input") end
if not ... then error end 的这种组合非常常见,以至于 Lua 有一个专门用于此工作的内置函数,称为 assert
    print "enter a number:"
    n = assert(io.read("*number"), "invalid input")
assert 函数检查其第一个参数是否不为假,并简单地返回该参数;如果参数为假(即,falsenil),assert 会引发错误。它的第二个参数(消息)是可选的,因此如果您不想在错误消息中说任何内容,则不必说。但是,请注意,assert 是一个常规函数。因此,Lua 始终在调用函数之前计算其参数。因此,如果您有类似以下内容:
    n = io.read()
    assert(tonumber(n),
           "invalid input: " .. n .. " is not a number")
即使 n 是一个数字,Lua 也会始终执行连接。在这种情况下,最好使用显式测试。

当一个函数发现意外情况(一个异常)时,它可以采取两种基本行为:它可以返回一个错误代码(通常为nil),或者它可以引发一个错误,调用error函数。在这两种选择之间没有固定的规则,但我们可以提供一个通用指南:一个容易避免的异常应该引发一个错误;否则,它应该返回一个错误代码。

例如,让我们考虑sin函数。当在表上调用时,它应该如何表现?假设它返回一个错误代码。如果我们需要检查错误,我们必须写类似这样的东西

    local res = math.sin(x)
    if not res then     -- error
      ...
然而,我们可以在调用函数之前轻松地检查这个异常
    if not tonumber(x) then     -- error: x is not a number
      ...
然而,通常我们既不检查sin调用的参数也不检查结果;如果参数不是一个数字,这可能意味着我们的程序中有些东西出了问题。在这种情况下,停止计算并发出错误消息是最简单、最实用的处理异常的方法。

另一方面,让我们考虑io.open函数,它打开一个文件。当被调用来读取一个不存在的文件时,它应该如何表现?在这种情况下,没有简单的方法可以在调用函数之前检查异常。在许多系统中,了解文件是否存在唯一的方法是尝试打开它。因此,如果io.open由于外部原因(例如"文件不存在""权限被拒绝")而无法打开文件,它将返回nil,外加一个带有错误消息的字符串。通过这种方式,您有机会以适当的方式处理这种情况,例如向用户询问另一个文件名

    local file, msg
    repeat
      print "enter a file name:"
      local name = io.read()
      if not name then return end   -- no input
      file, msg = io.open(name, "r")
      if not file then print(msg) end
    until file
如果您不想处理这种情况,但仍然想安全起见,您只需使用assert来保护操作
    file = assert(io.open(name, "r"))
这是一个典型的 Lua 惯用法:如果io.open失败,assert将引发一个错误。
    file = assert(io.open("no-file", "r"))
      --> stdin:1: no-file: No such file or directory
请注意,作为io.open的第二个结果的错误消息将作为assert的第二个参数。