0Pricing
Lua Academy · 课时

error() 函数

使用 error() 抛出错误,并理解错误级别和错误消息。

error() 函数 是 CoddyKit 上的免费 Lua Academy 课时。 这是第 1 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Lua Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Lua Academy 课程共包含 4 节课。

引发错误

error(message, level) 引发 Lua 错误。执行会停止,错误会沿调用栈向上传播,直到被 pcall 或 xpcall 捕获,或者终止程序。消息可以是任意值,例如字符串、表或数字。

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

print(divide(10, 2))    -- 5.0
-- divide(10, 0)        -- ERROR: division by zero

错误级别

error() 的第二个参数控制错误报告的位置。级别 1(默认值)指向 error() 调用本身。级别 2 指向调用者。级别 0 不添加位置信息。在库函数中使用级别 2,可以将错误归因于用户代码。

local function assertPositive(n, name)
  if n <= 0 then
    error((name or "value") .. " must be positive, got " .. n, 2)
    -- level 2: blame the caller, not this function
  end
  return n
end

local function compute(x)
  assertPositive(x, "x")   -- error points here if x <= 0
  return math.sqrt(x)
end

compute(-5)  -- error: "x must be positive, got -5" at compute() call

使用表对象表示错误

将表作为错误值传递,可以让调用者检查结构化的错误信息,包括错误代码、消息和上下文。这比单纯的字符串提供更多信息,也支持以编程方式处理错误。

local function openDB(host, port)
  if port < 1 or port > 65535 then
    error({code="INVALID_PORT", port=port,
           msg="port out of range: " .. port})
  end
  -- ... connect
  return {host=host, port=port}
end

local ok, err = pcall(openDB, "localhost", -1)
if not ok and type(err) == "table" then
  print("Code:", err.code)    -- INVALID_PORT
  print("Port:", err.port)    -- -1
  print("Msg:", err.msg)
end

将 assert() 作为简写

assert(v, msg) 等价于 if not v then error(msg, 2) end; return v, ...。它是检查前置条件的惯用方式。如果 v 为真值,assert 会返回它的所有参数(便于链式调用)。

local function sqrt(n)
  assert(type(n) == "number", "expected number, got " .. type(n))
  assert(n >= 0, "sqrt of negative: " .. n)
  return math.sqrt(n)
end

print(sqrt(16))    -- 4.0
print(sqrt(2))     -- 1.4142...
-- sqrt("hi")      -- ERROR: expected number, got string

error() 与 return nil,err

表示失败有两种约定:error()(异常式)或 return nil, msg(函数式)。对于真正意外的情况(编程错误、违反约定),请使用 error()。对于预期内的失败(文件未找到、网络超时),请使用 nil, msg。

-- Exception style (programming error)
local function mustExist(t, key)
  local v = t[key]
  if v == nil then error("required key missing: " .. key, 2) end
  return v
end

-- Functional style (expected failure)
local function findUser(id)
  -- ... database query
  return nil, "user not found"  -- expected: user may not exist
end

元方法中的错误

元方法内部也可以引发错误。如果元方法出错,错误会传播到触发该操作的代码处(例如某个算术表达式)。请始终在元方法中防范无效输入。

local SafeDiv = {}
SafeDiv.__index = SafeDiv

SafeDiv.__div = function(a, b)
  if b.value == 0 then
    error("SafeDiv: division by zero", 2)
  end
  return SafeDiv.new(a.value / b.value)
end

function SafeDiv.new(v)
  return setmetatable({value=v}, SafeDiv)
end

local a = SafeDiv.new(10)
local b = SafeDiv.new(0)
local ok, err = pcall(function() return a / b end)
print(ok, err)

自定义错误类型

请创建一个用于构建带类型错误对象的辅助函数。请包含类型标签,以便调用方区分不同种类的错误,并分别进行适当处理。

local function newError(kind, msg, extra)
  return setmetatable(
    {kind=kind, message=msg, extra=extra},
    {__tostring = function(e)
      return "[" .. e.kind .. "] " .. e.message
    end}
  )
end

local E = {
  notFound = function(name) return newError("NOT_FOUND","not found: "..name) end,
  badInput = function(msg)  return newError("BAD_INPUT", msg) end,
}

local ok, err = pcall(error, E.notFound("config.json"))
if not ok then print(err.kind, err.message) end

错误传播

当一个函数调用另一个发生错误的函数时,错误会自动沿调用栈向上传播。您不需要重新抛出错误,只需不要捕获它即可。只有在能够有意义地处理或报告错误的层级捕获错误。

local function step3() error("step3 failed") end
local function step2() step3() end
local function step1() step2() end

local ok, err = pcall(step1)
if not ok then
  -- err includes the source location
  print("Caught at top level:", err)
end
-- Error: input:1: step3 failed

使用 debug.traceback 获取调用栈跟踪

普通的 error() 只能提供一行上下文信息。若要获取完整的调用栈跟踪,请将 debug.traceback(msg) 用作错误值。这通常在 xpcall 处理函数中完成。

local function buggy()
  local t = nil
  return t.field   -- nil indexing: error
end

local ok, err = xpcall(buggy, function(e)
  return debug.traceback(e, 2)  -- full stack trace
end)

if not ok then
  print(err)  -- full traceback
end

重新抛出错误

有时您会捕获错误以添加上下文,然后重新抛出它。重新抛出字符串错误时,请使用 error(err, 0)(级别 0),以避免在已经格式化的消息前再次添加位置前缀。

local function loadAndParse(path)
  local ok, err = pcall(function()
    local f = io.open(path, "r")
    if not f then error("cannot open: " .. path) end
    local content = f:read("a")
    f:close()
    return content
  end)
  if not ok then
    error("loadAndParse failed: " .. err, 0)  -- re-raise with context
  end
end

local ok2, msg = pcall(loadAndParse, "missing.txt")
print(ok2, msg)

快速检查

error("msg", 2) 中的级别 2 表示什么?

回顾:error()

总结:

  • error(msg, level) — 抛出错误;级别 2 将责任归于调用方
  • 使用表表示包含类型和上下文的结构化错误
  • assert(v, msg) — 符合惯用写法的前置条件检查
  • 对于程序缺陷使用 error();对于预期失败返回 nil+err
  • 使用 error(err, 0) 重新抛出,以保留消息格式

常见问题解答

「error() 函数」课时是免费的吗?

是的 — 「error() 函数」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Lua Academy 课程的其余内容,请升级到 CoddyKit PRO。 Lua Academy 课程共包含 4 节课。

「error() 函数」这节课中我会学到什么?

使用 error() 抛出错误,并理解错误级别和错误消息。 你通过在浏览器中直接运行的动手代码来练习 Lua Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 Lua Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 Lua Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 1 节课,共 4 节。

「error() 函数」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 Lua Academy 课中编写并运行代码吗?

能。每节 Lua Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. error() 函数
  2. 使用 pcall 进行受保护调用
  3. xpcall 和消息处理器
  4. 结构化错误对象
← 返回 Lua Academy