一、基础核心:pcall
pcall 是 Lua 错误处理的基石。它让你能以一种"安全"的方式来执行一个函数。如果函数执行成功,pcall 会返回 true 以及函数的所有返回值;如果函数执行过程中发生了错误,它不会让程序崩溃,而是会捕获这个错误,返回 false 以及错误信息。
pcall 的语法模式: local success, result_or_error = pcall(function_to_call, arg1, arg2, ...)
success:一个布尔值。true表示成功,false表示失败。result_or_error:- 如果
success为true,这里是function_to_call的返回值。 - 如果
success为false,这里是错误信息字符串。
- 如果
示例:
lua
-- 1. 一个会成功的例子
local function calculate_ratio(a, b)
if b == 0 then
-- 我们将在这里主动制造一个错误
error("division by zero")
end
return a / b
end
-- 2. 尝试调用一个会成功的计算
local success, result = pcall(calculate_ratio, 10, 2)
if success then
print("Success! Result:", result) -- 输出: Success! Result: 5.0
else
print("Failure! Error:", result)
end
-- 3. 尝试调用一个会失败的计算
local success_fail, err_msg = pcall(calculate_ratio, 10, 0)
if success_fail then
print("Success! Result:", success_fail)
else
-- 程序不会崩溃,而是优雅地进入这个分支
print("Failure! Error:", err_msg) -- 输出: Failure! Error: .../demo.lua:5: division by zero
end
pcall 是处理那些你无法控制的外部因素(如文件IO、网络请求、加载用户脚本)的理想工具。
二、主动抛出错误:error() 与 assert()
1. error(message, level)
error() 函数用于中断当前函数的执行,并向上传播一个错误。
message:你想要报告的错误信息。level(可选):一个数字,用于指定错误发生的位置。默认是1(error被调用的位置)。2则指向调用error的那个函数的位置,这在编写工具库时非常有用。
示例:
lua
function set_age(person, age)
-- 使用 assert 来验证参数的合法性
assert(type(person) == "table", "Expected first argument to be a table")
if age <= 0 or type(age) ~= "number" then
error("Age must be a positive number!!!")
end
person.age = age
end
local player = {}
--set_age(player, 25) -- 成功
-- 尝试传递非法参数
-- 我们可以用 pcall 来捕获 assert 抛出的错误
local ok, err = pcall(set_age, player, -1)
if not ok then
print("Caught an error:", err)
-- 输出: Caught an error: .../demo.lua:4: Age must be a positive number
end
2. assert(condition, message)
assert() 是一个更常用、更简洁的工具。它检查第一个参数 condition 是否为"真"(即不为 false 或 nil)。
- 如果为真,
assert什么也不做,并返回所有参数。 - 如果为假,
assert就会用第二个参数message作为错误信息来调用error()。
示例:
lua
function set_age(person, age)
-- 使用 assert 来验证参数的合法性
assert(type(person) == "table", "Expected first argument to be a table")
assert(type(age) == "number" and age > 0, "Age must be a positive number")
person.age = age
end
local player = {}
set_age(player, 25) -- 成功
-- 尝试传递非法参数
-- 我们可以用 pcall 来捕获 assert 抛出的错误
local ok, err = pcall(set_age, player, "twenty")
if not ok then
print("Caught an error:", err)
-- 输出: Caught an error: .../demo.lua:4: Age must be a positive number
end
三、高级处理:xpcall 与堆栈回溯
有时候,仅仅知道错误信息是不够的,我们还想知道错误是在哪个函数调用链中发生的。这时就需要 xpcall。
xpcall xpcall 类似 pcall,但它允许你提供一个错误处理函数(Error Handler Function)。当错误发生时,Lua 会在"展开堆栈"之前,先调用你提供的这个处理函数,并将原始的错误信息作为参数传给它。
这给了我们一个黄金机会,去调用 debug.traceback() 来获取详细的调用堆栈。
示例:
lua
local function error_handler(err_msg)
-- 这是我们的自定义错误处理器
-- 它接收原始错误信息,并附加堆栈跟踪信息
return debug.traceback("Error: " .. tostring(err_msg), 2)
end
local function level_2()
-- 这个函数会出错
local a = nil
return a.value
end
local function level_1()
level_2()
end
-- 使用 xpcall 来包裹调用
local success, result = xpcall(level_1, error_handler)
if not success then
print("xpcall caught an error:")
print(result) -- 打印包含了完整堆栈跟踪的错误信息
end
运行上述代码,你将得到不仅仅是"attempt to index a nil value (local 'a')",还会有一个清晰的调用路径:xpcall -> level_1 -> level_2。这对于调试复杂问题至关重要。
结语
点个赞,关注我获取更多实用 Lua 技术干货!如果觉得有用,记得收藏本文!