Error handling 正在使用lua';s错误(..,级别)反模式?
Lua5.1的API提供了一个函数,它接受一个字符串(错误消息)和一个“级别” 我的理解是,Error handling 正在使用lua';s错误(..,级别)反模式?,error-handling,lua,lua-5.1,Error Handling,Lua,Lua 5.1,Lua5.1的API提供了一个函数,它接受一个字符串(错误消息)和一个“级别” 我的理解是,level允许您向上移动调用堆栈,因此可以提供更好的错误报告,尤其是在以API形式交付模块时 例如,假设用户使用x=nil调用api_函数(x)。这将是一个错误,但API直到对其代码进行了相当多的修改后才知道 它可能导致此调用堆栈: api_function(x) : user_file.lua:30 -> api_function
level
允许您向上移动调用堆栈,因此可以提供更好的错误报告,尤其是在以API形式交付模块时
例如,假设用户使用x=nil
调用api_函数(x)
。这将是一个错误,但API直到对其代码进行了相当多的修改后才知道
它可能导致此调用堆栈:
api_function(x) : user_file.lua:30
-> api_function : api.lua:20
-> some_function : api.lua:250
-> handle_when_x_string : api_string.lua:20
-> error("value is nil") : api_string.lua:66
如前所述,用户将看到类似于api\u字符串的内容。lua:66错误:值为nil
,而他们真正想看到的是“nice”错误,user\u文件。lua:30错误:值为nil
。(“这是我的错还是API中的错误?”)
现在,我们可以将代码更改为“弹出调用堆栈”
这将返回“nice”错误,但是,想象一下,当\u x_string更直接地调用handle\u时(除了糟糕的API设计)
现在我们的“流行级别”是不正确的。也许在这个例子中,它会简单地弹出到顶部并停止尝试,但“不正确的级别”的原则至少仍然令人不舒服,它甚至可能弹出用户导致错误的位置
我可以看到一些解决方案:
- 不要设置级别,只需假设用户足够聪明就能解决它
- 将api入口点(
api_函数
&另一个api_fn
)下方的任何内容包装在pcall中,捕获任何错误并使用已知的“良好”级别值重新冒泡
- 不要在较低的api函数中出错,始终
返回nil、error
或类似的模式,然后在api_函数中检查该模式,并根据需要执行操作
我的问题是:
- 返回错误的级别是否有问题?只是“是的,不管怎样”一个数字并希望它是好的,这看起来很糟糕
- 如果这是一个问题,什么时候设置该级别是一个好的做法(超过0可能会禁用位置报告)
- 哪种解决方案(如果有的话)是最佳实践?我应该如何编写更好的可维护代码?在pcall中包装似乎最简单,因为在测试时您仍然可以依赖“正常错误”,并且您的函数稍微简单一些,但在我看来,它似乎是一种反模式
在Lua中,使用error()
来“冒泡”错误是非常罕见的。更常见的是返回nil
,后跟一个描述错误的字符串,并让调用方决定函数失败是否是可以恢复的,是否应该重试,等等
与调用error()
(这只是一个普通的函数调用)相比,它的开销更小,因此性能更高
过度使用pcall
和转发错误是一个非常糟糕的主意。这只会让你的代码更难理解
返回错误的级别是否有问题?只是“是的,不管怎样”一个数字并希望它是好的,这看起来很糟糕
不,除了引起混乱。错误消息是有帮助的,如果你去“无论什么”他们不会。从实现的角度来看,没有风险:用于添加带有错误位置的消息。如果您走得太远,信息将被跳过:
函数foo()
错误(“我在哪里”,7)
结束
卢:我在哪里
--堆栈回溯:。。。
我通常只看到级别1和2,后者通常用于指示传递给函数的参数不正确。但这是我的观察,没有具体数据支持
至于剩下的问题,主要是基于意见。使用任何适合你和你的问题。error()
和nil
+消息都是合理的方法。您对它的研究做得很好,并且似乎对它有足够的理解来做出决定。首先,您需要区分由于错误的API调用导致的错误和代码中的实际错误
如果error
调用的目的是告诉API用户他们传递了错误的参数,那么您应该验证每个API函数中的参数,这样错误级别就可以知道,并且库的其他部分就知道它正在使用有效的参数。如果您最终得到一个复杂的验证函数层次结构,它们可以获取函数名和错误级别的参数。下面是一个关于如何使用错误级别的精心设计的示例:
local function lessThan100(x, funcName, errorLevel)
if x >=100 then
error(funcName .. ' needs a number less than 100', errorLevel)
end
end
local function numLessThan100(x, funcName, errorLevel)
if type(x) ~= 'number' then
error(funcName .. ' needs a number', errorLevel)
end
lessThan100(x, funcName, errorLevel + 1)
end
-- API function
local function printNum(x)
numLessThan100(x, 'printNum', 3)
print(x)
end
如果错误
调用代表代码中的错误,那么不要使用级别,因为您不知道是什么触发了错误。这似乎是一个针对特定目的而存在的特定工具的例子。
从:
error函数还有一个附加的第二个参数,它给出
报告错误的级别;有了它,你可以责怪别人
否则将导致错误。例如,假设您编写了一个函数及其
第一项任务是检查是否正确调用了它:
然后,有人用错误的参数调用您的函数:
Lua将它的手指指向您的函数——毕竟,调用错误的是foo——而不是真正的罪魁祸首,调用方。
要更正此错误,请通知error您正在报告的错误
在调用层次结构中的级别2上发生(级别1是您自己的
功能):
没有一种语言是完美的,有时设计者会创建一个强大且潜在危险的工具来解决特定的问题。(例如,C#反射)在这种情况下,其目的似乎是报告发出调用的函数中的错误API调用,而不是报告接收调用的函数。因此,根据文档,级别1和2是唯一的预期级别,但这并不意味着其他用例不存在
您已经强调了(mis)使用此语言功能可能会导致比解决问题更多的问题的方式(即使用错误代码误导用户)
another_api_fn(x) : user_file.lua:44
-> another_api_fn : api.lua:11
-> handle_when_x_string : api_string.lua:20
-> error("value is nil", 5) : api_string.lua:66
local function lessThan100(x, funcName, errorLevel)
if x >=100 then
error(funcName .. ' needs a number less than 100', errorLevel)
end
end
local function numLessThan100(x, funcName, errorLevel)
if type(x) ~= 'number' then
error(funcName .. ' needs a number', errorLevel)
end
lessThan100(x, funcName, errorLevel + 1)
end
-- API function
local function printNum(x)
numLessThan100(x, 'printNum', 3)
print(x)
end