構造化エラーオブジェクト
テーブルをエラーオブジェクトとして渡し、種類やコンテキストを伝えます。
「構造化エラーオブジェクト」はCoddyKit上の無料Lua Academyレッスンです。 これはレッスン4/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはLua Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Lua Academyコースには全4レッスンが含まれています。
構造化エラーを使う理由
単純な文字列エラーは、プログラムから扱いにくいものです。構造化されたエラーオブジェクト(テーブル)には、型情報やコンテキストデータを持たせることができ、呼び出し側で調査して処理できます。これにより、文字列解析を行わずにエラーに基づく振り分けが可能になります。
-- Plain string: hard to handle programmatically
error("database error: connection refused")
-- Structured: type + data
error({
type = "DatabaseError",
code = "CONN_REFUSED",
host = "localhost",
port = 5432,
message = "connection refused"
})エラーコンストラクタパターン
エラーの種類ごとにエラーファクトリ関数を作成します。ファクトリでは、type、message、および関連するコンテキストを一貫したフィールドとして持つテーブルを構築します。__tostring メタメソッドを使うと、エラーを見やすく表示できます。
local ErrorMT = {__tostring = function(e)
return string.format("[%s] %s", e.type, e.message)
end}
local function makeError(errType, msg, data)
local e = {type=errType, message=msg}
if data then for k,v in pairs(data) do e[k]=v end end
return setmetatable(e, ErrorMT)
end
local E = {
notFound = function(name) return makeError("NOT_FOUND","not found: "..name,{name=name}) end,
badInput = function(msg,field) return makeError("BAD_INPUT",msg,{field=field}) end,
}
local ok, err = pcall(error, E.notFound("user:42"))
print(tostring(err)) -- [NOT_FOUND] not found: user:42エラーオブジェクトの型チェック
エラーを捕捉した後、それが既知の type フィールドを持つテーブルかどうかを確認します。これにより、文字列パターンマッチングに頼らず、エラーの種類に応じて異なる復旧方法を振り分けられます。
local function handleRequest(fn)
local ok, err = pcall(fn)
if ok then return true end
if type(err) == "table" then
if err.type == "NOT_FOUND" then
print("404: " .. err.message)
elseif err.type == "BAD_INPUT" then
print("400: " .. err.message .. " (field: " .. (err.field or "?") .. ")")
else
print("500: unhandled error: " .. tostring(err))
end
else
print("500: " .. tostring(err))
end
return false
endエラー階層
is_a フィールドを確認するか、メタテーブルを使うことで、エラー階層を再現できます。子エラー型は親型のフィールドを継承し、詳細を必要としないコードからは親型として扱えます。
local function isError(e, errType)
if type(e) ~= "table" then return false end
return e.type == errType or e.parentType == errType
end
local function makeDbError(code, msg)
return {type="DbError:"..code, parentType="DbError", code=code, message=msg}
end
local err = makeDbError("TIMEOUT","query timed out")
print(isError(err, "DbError")) -- true
print(isError(err, "DbError:TIMEOUT")) -- true
print(isError(err, "NetworkError")) -- falseエラーのラップ
捕捉して再スローするときは、元のエラーを失わずにコンテキストを追加するため、エラーをラップします。ラッパーは独自の型を持ち、原因となった元のエラーを cause として保持します。
local function wrapError(msg, cause)
return {
type = "WrappedError",
message = msg,
cause = cause,
}
end
local function loadConfig(path)
local ok, err = pcall(function()
local f = assert(io.open(path,"r"))
local content = f:read("a")
f:close()
return content
end)
if not ok then
error(wrapError("failed to load config: "..path, err))
end
end
local ok2, e = pcall(loadConfig, "missing.cfg")
if not ok2 then
print(e.message)
print("Caused by:", tostring(e.cause))
endエラーコードとエラー型
一般的な規約は2つあります。エラーコード(HTTP ステータスコードのような数値)と、エラー型文字列(意味を表す名前)です。エラーコードは数値で比較しやすく、型文字列は自己説明的です。多くのシステムでは両方を使用します。
local STATUS = {OK=200, NOT_FOUND=404, SERVER_ERROR=500, BAD_REQUEST=400}
local function makeStatusError(status, msg)
return {status=status, message=msg, type="HTTPError"}
end
local function handleError(e)
if e.status == STATUS.NOT_FOUND then
print("Resource not found:", e.message)
elseif e.status >= 500 then
print("Server error:", e.message)
else
print("Error", e.status, e.message)
end
end
handleError(makeStatusError(404, "user not found"))エラースタック(原因チェーン)
あるエラーが別のエラーによって引き起こされた場合は、それらをチェーンでつなぎます。これにより、アプリケーションの各層で何が起きたのかを全体的に把握できます。チェーンをアンラップして、エラーの経緯全体をログに記録したり表示したりします。
local function unwindCause(e, depth)
depth = depth or 0
local pad = string.rep(" ", depth)
if type(e) == "table" then
print(pad .. (e.type or "Error") .. ": " .. (e.message or "?"))
if e.cause then unwindCause(e.cause, depth+1) end
else
print(pad .. tostring(e))
end
end
local inner = {type="IoError", message="permission denied"}
local outer = {type="ConfigError", message="cannot load config", cause=inner}
unwindCause(outer)
-- ConfigError: cannot load config
-- IoError: permission deniedコールバックコンテキストのエラー
コールバック(イベントハンドラーやイテレーター)の内部でエラーが発生すると、コールバックの呼び出し元へ伝播します。pcall で捕捉し、どのコールバックが失敗したのかというコンテキストを添えて報告します。
local function runCallbacks(callbacks, data)
local errors = {}
for name, fn in pairs(callbacks) do
local ok, err = pcall(fn, data)
if not ok then
errors[#errors+1] = {callback=name, error=err}
end
end
return errors
end
local cbs = {
validate = function(d) assert(d.name, "name required") end,
transform = function(d) d.name = d.name:upper() end,
}
local errs = runCallbacks(cbs, {})
for _, e in ipairs(errs) do
print(e.callback, "->", e.error)
endエラー詳細の表示
エラーオブジェクトを構造化された読みやすい形式で表示するヘルパーです。文字列エラーとテーブルエラーの両方を処理します。エラーをログに記録したりユーザーに表示したりする、アプリケーションの境界部分で役立ちます。
local function printError(err, prefix)
prefix = prefix or "Error"
if type(err) ~= "table" then
print(prefix .. ": " .. tostring(err))
return
end
print(prefix .. " [" .. (err.type or "unknown") .. "]")
print(" Message: " .. (err.message or "?"))
for k, v in pairs(err) do
if k ~= "type" and k ~= "message" and k ~= "cause" then
print(" " .. k .. ": " .. tostring(v))
end
end
if err.cause then printError(err.cause, " Caused by") end
end構造化エラーによるアサート
通常の文字列ではなく構造化されたエラーを発生させる、assertT(型付きエラーによるアサート)を作成します。これにより、呼び出し側で特定のエラー型を簡単にテストできます。
local function assertT(cond, errType, msg, data)
if not cond then
local e = {type=errType, message=msg}
if data then for k,v in pairs(data) do e[k]=v end end
error(e, 2)
end
return cond
end
local function createUser(name, age)
assertT(type(name)=="string", "BAD_INPUT", "name must be string", {field="name"})
assertT(age >= 0 and age <= 150, "BAD_INPUT", "invalid age", {field="age", value=age})
return {name=name, age=age}
end
local ok, err = pcall(createUser, "Alice", -5)
if not ok then print(err.type, err.field, err.value) end理解度チェック
文字列ではなくテーブルを error() に渡す主な利点は何ですか?
まとめ: 構造化エラー
まとめ:
- 構造化され、調査可能なエラーにはテーブルを
error()に渡します - type、message、および関連するコンテキストフィールドを含めます
- 読みやすい出力のために
__tostringを追加します - 原因を失わずにコンテキストを追加するため、エラーをラップします
- ハンドラーでは型で振り分けます。文字列パターンではなく
err.typeを確認します
よくある質問
「構造化エラーオブジェクト」レッスンは無料ですか?
はい。「構造化エラーオブジェクト」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Lua Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Lua Academyコースには全4レッスンが含まれています。
「構造化エラーオブジェクト」で何を学びますか?
テーブルをエラーオブジェクトとして渡し、種類やコンテキストを伝えます。 ブラウザで直接実行するハンズオンコードでLua Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
Lua Academyを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのLua Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン4/4です。
「構造化エラーオブジェクト」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このLua Academyレッスンでコードを書いて実行できますか?
はい。すべてのLua Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。
このコースのすべてのレッスン
- error()関数
- pcallによる保護付き呼び出し
- xpcallとメッセージハンドラー
- 構造化エラーオブジェクト