使用lua的error(..,level)是一种反模式吗?
Lua 5.1的API提供了一个[error()](https://www.lua.org/manual/5.1/manual.html#pdf-error)函数,它接受一个字符串(错误消息)和 一个“级别”。
我的理解是,level使您可以向上移动调用堆栈,因此可以在提供模块作为API时提供更好的错误报告。
例如,想象用户使用x = nil调用api_function(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_string.lua:66 error: value is nil的东西,而他们真正想看到的是“好”错误user_file.lua:30 error: value is nil。(“这个错误是我的错还是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”,5):api_string.lua:66
这将返回“好”的错误,_但是_,想象一下您也可以直接调用handle_when_x_string(除了API设计不好之外),
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
现在我们的“流行水平”是不正确的。也许在这个例子中,它只会弹到顶部并停止尝试,但“不正确的级别”原则至少仍然感到不舒服,它甚至可能会“弹出”用户引起的错误位置。
我能想到一些解决方案:
- 不设置级别,只是假设用户足够聪明,可以解决它。
- 在您的API入口点(
api_function和another_api_fn)下面的任何东西中都包装一个pcall,在其中捕获任何错误并具有已知的“良好”级别值重新泡沫。 - 永远不要在较低的api函数中出现错误,始终返回
nil,error或类似的模式,然后在api_function中检查那些并按需要执行。
我的问题是:
- _是否_返回错误级别不正确?它似乎很粗鲁,只是在那里“无论怎样”一个数字,并希望这是好的。
- 如果这是一个问题,那么什么时候设置级别会成为一种好的实践(除了可能禁用位置报告的0)
- 如果有的话,这些解决方案中哪些是最佳实践?我应该如何编写更好的可维护代码?在pcall中包装似乎是最简单的方法,因为在测试时您仍然可以依赖于“常规错误”,而您的函数略微简单,但在我的头脑中,它似乎是一种反模式。
返回错误级别是否成问题?只是将一个数字扔进去然后希望它正确似乎很不专业。
并不会有问题,除了会造成混淆。错误信息是为了帮助解决问题的,如果你漫不经心处理错误,那么它们也就无法帮到你了。从实现的角度来看,这种做法没有风险:luaB_error 使用 luaL_where 来添加错误位置相关信息。如果你传入了错误的级别,这些信息也就只是被简单地忽略了:
function foo ()
error("where am I", 7)
end
foo() -- lua: where am I
-- stack traceback: ...
通常我只看到过级别为 1 和 2 的错误,后者通常用于指示传递给函数的参数无效。但这只是我的经验,没有什么数据支持。
至于剩下的问题,主要还是基于个人意见的。使用任何适合你和你的问题的方法。error() 和 nil 加信息两种方式都是有道理的。你在调研方面做得很好,看起来已经足够理解并且可以自行决定了。
首先,您需要区分由于错误的API调用和代码中的实际错误而导致的错误。
如果“error”调用的目的是告诉API用户他们传递了错误的参数,则应该在每个API函数中验证参数,以便错误级别可知,并且您的库的其余部分知道它正在使用有效的参数。如果最终得到一个复杂的验证函数层次结构,它们可以为函数名称和错误级别传递参数。以下是一个非常人为的示例,说明如何使用错误级别:
local function lessThan100(x, funcName, errorLevel)
if x >=100 then
error(funcName .. ' 需要小于100的数字', errorLevel)
end
end
local function numLessThan100(x, funcName, errorLevel)
if type(x) ~= 'number' then
error(funcName .. ' 需要数字', errorLevel)
end
lessThan100(x, funcName, errorLevel + 1)
end
-- API函数
local function printNum(x)
numLessThan100(x, 'printNum', 3)
print(x)
end
如果“error”调用表示代码中的错误,则不要使用级别,因为您无法知道何时会触发错误。
这似乎是一个为特定目的而存在的特定工具的例子。根据文档:
错误函数有一个额外的第二个参数,它指定了应该在哪个级别上报告错误。你可以责备别人犯的错误。例如,假设你编写了一个函数,它的第一个任务是检查它是否被正确调用:
然后,有人使用错误的参数调用了你的函数:
Lua将指向你的函数 - 终究是foo调用了error - 而不是指向真正的罪魁祸首,调用者。 要纠正这个问题,你需要告诉error,你正在报告在调用层级结构中发生的错误是在第二个级别上发生的(第一个级别是你自己的函数)。
没有一个语言是完美的,有时设计者会为了解决一个特定的问题而创建一个强大而潜在危险的工具。(例如C#的反射)在这种情况下,看起来意图是在做出调用的函数中报告错误的不良API调用,而不是在接收它的函数中。因此,根据文档,级别1和2是唯一的预期级别,但这并不意味着其他用例不存在。
你已经强调了(误)使用这个语言特性可能会引起比它解决的问题更多的问题(即用一个不合理的错误代码误导用户)。好的做法问题通常相当主观,但我们的工作是编写良好,可维护,无漏洞的代码,无论使用何种语言。语言设计师显然认为在你的工具箱中放置这个工具是一个好主意。你可以从手册开始阅读,但除此之外,你需要理解这个工具,并善加利用。如果你觉得这个工具不应该存在,或者你只是不感到舒适使用它,那么就坚持使用你提到的另一种替代方法。
- Lua 虚拟机加密load(string.dump(function)) 后执行失败问题如何解决
- 我想创建一个 Nginx 规则,禁止访问
- 如何将两个不同的lua文件合成一个 东西有点长 大佬请耐心看完 我是小白研究几天了都没搞定
- 如何在roblox studio中1:1导入真实世界的地形?
- 求解,lua_resume的第二次调用继续执行协程问题。
- 【上海普陀区】内向猫网络招募【Skynet游戏框架Lua后端程序员】
- SF爱好求教:如何用lua实现游戏内调用数据库函数实现账号密码注册?
- Lua实现网站后台开发
- LUA错误显式返回,社区常见的规约是怎么样的
- lua5.3下载库失败
- 请问如何实现文本框内容和某个网页搜索框内容连接,并把网页输出来的结果反馈到另外一个文本框上
- lua lanes多线程使用
- 一个kv数据库
- openresty 有没有比较轻量的 docker 镜像
- 想问一下,有大佬用过luacurl吗
- 在Lua执行过程中使用Load函数出现问题
- 为什么 neovim 里没有显示一些特殊字符?
- Lua比较两个表的值(不考虑键的顺序)
- 有个lua简单的项目,外包,有意者加微信 liuheng600456详谈,最好在成都
- 如何在 Visual Studio 2022 中运行 Lua 代码?

在Lua中使用
error()“向上冒泡”错误非常不常见。更常见的是返回nil,然后跟随一个描述错误的字符串,让调用者决定函数是否失败,是否可以恢复以及是否应该重试等等。这也更具有性能,因为它比调用
error()少了一些额外的开销(它只是一个普通函数调用)。过度使用
pcall和转发错误是一个非常糟糕的想法。这只会使你的代码更难以跟踪。