《Lua快速入门》7.错误处理与调试

Lua 提供了简单的错误处理机制,主要通过 `pcall` 和 `xpcall` 函数实现。错误处理机制可以捕获运行时错误,并允许程序在错误发生时执行特定的操作。

错误处理与调试

1. 错误捕获与处理

1.1 Lua 的错误处理机制

Lua 提供了一种简单的错误处理机制,通过 pcall 和 xpcall 函数捕获和处理运行时错误。保护调用会捕获错误并把控制权交回调用方;发生错误的函数会在该处退出,不会继续执行错误语句之后的代码。调用方仍须检查成功标志。

1.2 pcall 函数

pcall(Protected Call)用于以保护模式调用一个函数,并捕获可能的错误。

1.2.1 pcall 的基本用法

pcall 接受函数及其参数。成功时返回 true 和原函数的全部返回值;失败时返回 false 与原始错误对象,错误对象未必是字符串。它不固定只返回两个值,保留中间的 nil 可使用 table.pack/table.unpack。

local status, result = pcall(function()
    error("Something went wrong!")
end)
if status then
    print("Success:", result)
else
    print("Error:", result)
end
-- 输出: Error: Something went wrong!
1.2.2 pcall 的嵌套使用

pcall 可以嵌套使用,以处理多层函数调用中的错误。

local function riskyFunction()
    error("Risky operation failed!")
end

local status, result = pcall(function()
    pcall(riskyFunction)
end)
print(status, result)  -- 输出: true  nil

这里外层 true 只表示外层函数正常返回:内层 pcall 已经捕获错误,但它的失败结果被丢弃了。如果需要逐层传递结果,应承接上例这样返回内层结果:

local outerOk, innerOk, err = pcall(function()
    return pcall(riskyFunction)
end)
assert(outerOk == true and innerOk == false)
assert(type(err) == "string" and err:find("Risky operation failed!", 1, true))
print(outerOk, innerOk, type(err)) -- true false string

1.3 xpcall 函数

xpcall 是 pcall 的扩展版本,允许指定消息处理函数。它与 debug.sethook 的调试钩子是两种机制。

1.3.1 xpcall 的基本用法

xpcall(f, msgh, ...) 的前两个参数是:

  • 第一个参数是要执行的函数。
  • 第二个参数是消息处理函数。后续参数传给 f;处理函数的返回值成为失败结果,只打印而不 return 会使错误详情变成 nil。
local function errorHandler(err)
    return debug.traceback(tostring(err), 2)
end

local status, result = xpcall(function()
    error("Something went wrong!")
end, errorHandler)
print(status, result)  -- false,随后是带调用栈的错误文本;路径和行号随脚本变化
1.3.2 使用调试信息

xpcall 的错误处理函数可以访问调试信息,如调用栈。

local function errorHandler(err)
    print("Error:", err)
    print(debug.traceback())
end

xpcall(function()
    error("An error occurred!")
end, errorHandler)

1.4 自定义错误

Lua 允许通过 error 函数抛出自定义错误。

local function divide(a, b)
    if b == 0 then
        error("Division by zero!")
    end
    return a / b
end

local status, result = pcall(divide, 10, 0)
if not status then
    print("Error:", result)
end
-- 输出: Error: Division by zero!

2. 调试技巧

2.1 调试工具

Lua 提供了内置的调试库 debug,支持调试信息的获取和处理。

2.1.1 debug.traceback

debug.traceback 用于获取当前的调用栈信息。

local function foo()
    print(debug.traceback("Traceback:"))
end

local function bar()
    foo()
end

bar()
2.1.2 debug.getinfo

debug.getinfo 用于获取函数的信息,如文件名、行号、函数名等。

local function foo()
    local info = debug.getinfo(1)
    print("Function:", info.name)
    print("Line:", info.currentline)
end

foo()

2.2 调试钩子

调试钩子(Debug Hook)是一种在程序执行过程中插入调试代码的机制。

2.2.1 设置调试钩子

使用 debug.sethook 设置调试钩子。

local function hook(event, line)
    print("Event:", event, "Line:", line)
end

debug.sethook(hook, "l")  -- 在每行代码执行时调用钩子

local function foo()
    print("Inside foo")
end

foo()
2.2.2 调试钩子的事件类型
  • "call":函数调用时触发。
  • "return":函数返回时触发。
  • "line":每行代码执行时触发。
  • "count":每执行指定数量的指令时触发。

2.3 断点调试

Lua 本身不支持断点调试,但可以通过调试库实现类似功能。

2.3.1 使用 debug.debug

debug.debug 启动一个交互式调试器。

local function foo()
    debug.debug()
    print("Inside foo")
end

foo()
2.3.2 使用外部调试工具
  • ZeroBrane Studio:一个支持 Lua 的集成开发环境(IDE),提供断点调试功能。
  • Lua Debugger (ldb):一个命令行调试工具。

2.4 日志调试

在代码中添加日志输出,是一种简单有效的调试方法。

2.4.1 使用 print 输出日志
local function foo()
    print("Entering foo")
    -- 业务逻辑
    print("Exiting foo")
end

foo()
2.4.2 使用日志库
  • LuaLogging:一个功能强大的日志库,支持多种日志级别和输出格式。
local logging = require("logging")
local logger = logging.file("app.log")

logger:info("Starting application")
logger:error("An error occurred!")

3. 练习与可判定验收

  1. 让受保护函数返回 "a", nil, "c"。验收:table.pack(pcall(...)).n == 4,第 3 项仍是 nil;不能只用两个变量接收后宣称保留全部结果。
  2. 给函数传入一个 table 作为错误对象,在 error 后再放一个赋值语句。验收:pcall 返回的错误对象与原 table 相同,后续赋值没有执行。
  3. 对比嵌套例子的两种写法。验收:丢弃内层结果时外层返回 true/nil;显式 return 内层结果后得到 true/false/错误详情。不能以外层 true 推断业务成功。

2026-10-17 在临时构建的 Lua 5.4.7 / 5.5.1 验证本节保护调用、消息处理器、多个结果、错误对象和上述解题路径。交互式 debug.debug、外部 IDE 和日志库不在这组复跑范围。定义见 Lua 官方 pcall / xpcall 参考 。

4. 总结

Lua 的错误处理机制简单而强大,通过 pcall 和 xpcall 可以捕获和处理运行时错误。调试技巧包括使用调试库、调试钩子、断点调试和日志调试等方法。掌握这些技术,可以帮助开发者快速定位和修复代码中的问题。


继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「lua」更多文章

  1. Lua 时间日期处理与时区
  2. Lua 在嵌入式与 IoT 中的开发实践
  3. Lua 与 WebAssembly 互操作