error() / assert()
『Lua』では『error()』関数で意図的にエラーを発生させ、『assert()』関数で条件を検査してプログラムの不正な状態を早期に検出できます。どちらも実行を中断してエラーメッセージを通知するため、デバッグや防御的プログラミングに欠かせない機能です。
構文
-- -----------------------------------------------
-- error() — エラーを発生させます
-- -----------------------------------------------
error(message) -- メッセージ文字列でエラーを発生させます
error(message, level) -- level でエラー位置をどの呼び出し段階に帰属させるかを指定します
-- level=1(デフォルト): error() を呼んだ場所
-- level=2: error() を呼んだ関数の呼び出し元
-- level=0: 位置情報なし
-- -----------------------------------------------
-- assert() — 条件が偽のときエラーを発生させます
-- -----------------------------------------------
assert(条件) -- 条件が false / nil のとき "assertion failed!" エラー
assert(条件, メッセージ) -- 失敗時のエラーメッセージを指定できます
assert(条件, メッセージ, ...) -- 第3引数以降の値もそのまま返却されます
-- -----------------------------------------------
-- pcall() と組み合わせてエラーを捕捉します
-- -----------------------------------------------
local ok, err = pcall(function()
error("エラーメッセージ")
end)
-- ok が false のときエラーが発生しています
-- err にエラーメッセージが格納されます
構文一覧
| 構文/形式 | 概要 |
|---|---|
error(msg) | 文字列・テーブル・任意の値を引数にとり、実行を中断してエラーを発生させます。『pcall()』や『xpcall()』で捕捉できます。 |
error(msg, level) | エラー位置情報をどの呼び出し段階に帰属させるか指定します。『level=0』で位置情報なし、『level=2』で呼び出し元を示します。 |
assert(v) | v が『false』または『nil』のとき "assertion failed!" エラーを発生させます。それ以外のときは v をそのまま返します。 |
assert(v, msg) | 条件が偽のときに独自のエラーメッセージで失敗させます。入力値の検証やモジュールの前提条件チェックによく使います。 |
assert(v, msg, ...) | 成功時は v, msg, ... をすべてそのまま返します。戻り値チェックと値の受け取りを一行で書けます。 |
pcall(f, ...) | 関数 f を保護モードで呼び出します。エラーが発生しても停止せず false, errMsg を返します。 |
xpcall(f, handler, ...) | 保護モードで呼び出し、エラー時にハンドラ関数を実行します。スタックトレースの取得などに使います。 |
サンプルコード
psychopass_error_assert.lua
-- psychopass_error_assert.lua — Lua の error() / assert() のサンプルです
-- PSYCHO-PASS のキャラクターを使って
-- エラーの発生・捕捉・アサーションを確認します
-- -----------------------------------------------
-- error() の基本的な使い方(pcall で捕捉)
-- -----------------------------------------------
-- 犯罪係数が閾値を超えたときにエラーを発生させる関数です
local function checkCrimeCoefficient(name, coefficient)
if coefficient >= 300 then
-- level=2 で呼び出し元の行番号をエラー位置に帰属させます
error(name .. " の犯罪係数 " .. coefficient .. " が危険域を超えました", 2)
end
return name .. " の犯罪係数: " .. coefficient .. " (安全)"
end
print("=== error() の基本 ===")
-- 正常ケース
local ok, result = pcall(checkCrimeCoefficient, "常守朱", 28)
if ok then
print(result)
end
-- エラーケース(犯罪係数が 300 以上)
ok, result = pcall(checkCrimeCoefficient, "槙島聖護", 350)
if not ok then
print("エラー捕捉: " .. result)
end
print("")
-- -----------------------------------------------
-- error() にテーブルを渡す(構造化エラー)
-- -----------------------------------------------
-- エラー情報をテーブルで返すことで詳細な情報を持たせます
local function scanDominator(user, authLevel)
if authLevel < 3 then
error({
code = "AUTH_DENIED",
message = user .. " はドミネーターの使用権限がありません",
level = authLevel,
})
end
return user .. " がドミネーターを起動しました"
end
print("=== error() にテーブルを渡す ===")
ok, result = pcall(scanDominator, "常守朱", 5)
if ok then
print(result)
end
ok, result = pcall(scanDominator, "宜野座伸元", 2)
if not ok then
-- エラーオブジェクトがテーブルのときは各フィールドにアクセスできます
if type(result) == "table" then
print("エラーコード: " .. result.code)
print("エラー詳細: " .. result.message)
print("認証レベル: " .. result.level)
end
end
print("")
-- -----------------------------------------------
-- assert() の基本的な使い方
-- -----------------------------------------------
-- 監視対象の登録を行います。名前と係数は必須とします
local function registerSubject(name, coefficient)
assert(type(name) == "string", "name は文字列でなければなりません")
assert(type(coefficient) == "number", "coefficient は数値でなければなりません")
assert(coefficient >= 0, "coefficient は 0 以上でなければなりません")
return string.format("登録完了: %s(係数: %d)", name, coefficient)
end
print("=== assert() の基本 ===")
-- 正常ケース
ok, result = pcall(registerSubject, "霜月美佳", 45)
if ok then print(result) end
ok, result = pcall(registerSubject, "東金朔夜", 195)
if ok then print(result) end
-- assert 失敗ケース(係数が負数)
ok, result = pcall(registerSubject, "縢秀星", -10)
if not ok then
print("アサーション失敗: " .. result)
end
print("")
-- -----------------------------------------------
-- assert() の戻り値をそのまま利用する
-- -----------------------------------------------
-- io.open などの「失敗時に nil, errMsg を返す」関数と組み合わせます
-- ここでは成功・失敗を模倣したラッパーで確認します
-- 監視官のプロファイルをロードします(見つからないときは nil を返します)
local function loadProfile(name)
local profiles = {
["常守朱"] = { division = "公安局刑事課一係", rank = "監視官" },
["宜野座伸元"] = { division = "公安局刑事課一係", rank = "監視官" },
["霜月美佳"] = { division = "公安局刑事課一係", rank = "監視官" },
}
return profiles[name] -- 見つからなければ nil を返します
end
print("=== assert() の戻り値利用 ===")
-- 成功時: assert は第一引数をそのまま返すので profile に代入できます
ok, result = pcall(function()
local profile = assert(loadProfile("常守朱"), "プロファイルが見つかりません")
print("氏名: 常守朱 所属: " .. profile.division .. " 階級: " .. profile.rank)
end)
-- 失敗時: nil が返ると assert がエラーを発生させます
ok, result = pcall(function()
local profile = assert(loadProfile("牧島狡噛"), "プロファイルが見つかりません")
print(profile.division)
end)
if not ok then
print("エラー捕捉: " .. result)
end
print("")
-- -----------------------------------------------
-- xpcall() でスタックトレースを取得する
-- -----------------------------------------------
-- エラーハンドラ関数です(エラー発生時に呼ばれます)
local function errorHandler(err)
-- debug.traceback で呼び出し履歴を付加します
return "[エラーハンドラ] " .. tostring(err) .. "\n" .. debug.traceback("", 2)
end
print("=== xpcall() によるスタックトレース ===")
local function innerCheck(coefficient)
error("係数 " .. coefficient .. " で異常検知")
end
local function outerScan(name, coefficient)
print(name .. " をスキャン中…")
innerCheck(coefficient)
end
ok, result = xpcall(function()
outerScan("槙島聖護", 350)
end, errorHandler)
if not ok then
-- スタックトレースを含むエラー情報を表示します
print(result)
end
lua psychopass_error_assert.lua === error() の基本 === 常守朱 の犯罪係数: 28 (安全) エラー捕捉: psychopass_error_assert.lua:17: 槙島聖護 の犯罪係数 350 が危険域を超えました === error() にテーブルを渡す === 常守朱 がドミネーターを起動しました エラーコード: AUTH_DENIED エラー詳細: 宜野座伸元 はドミネーターの使用権限がありません 認証レベル: 2 === assert() の基本 === 登録完了: 霜月美佳(係数: 45) 登録完了: 東金朔夜(係数: 195) アサーション失敗: psychopass_error_assert.lua:52: coefficient は 0 以上でなければなりません === assert() の戻り値利用 === 氏名: 常守朱 所属: 公安局刑事課一係 階級: 監視官 エラー捕捉: psychopass_error_assert.lua:75: プロファイルが見つかりません === xpcall() によるスタックトレース === 槙島聖護 をスキャン中… [エラーハンドラ] psychopass_error_assert.lua:94: 係数 350 で異常検知 stack traceback: psychopass_error_assert.lua:94: in local 'innerCheck' psychopass_error_assert.lua:98: in local 'outerScan' psychopass_error_assert.lua:102: in function <psychopass_error_assert.lua:101> [C]: in function 'xpcall' psychopass_error_assert.lua:101: in main chunk [C]: in ?
よくあるミス
よくあるミス1: assert の第二引数をエラーオブジェクトとして使えると思い込む
『assert(v, msg)』の第二引数はエラーメッセージとして『error()』に渡されます。テーブルを渡しても文字列に変換されないため、構造化されたエラーオブジェクトとして捕捉することはできません。構造化エラーが必要な場合は『error()』を直接使います。
ng_example.lua
local ok, err = pcall(function()
assert(false, { code = "E001", message = "invalid input" })
end)
-- err はテーブルではなくテーブルのアドレス文字列になる
print(type(err), err)
string table: 0x55a7b12f0e10
構造化エラーを捕捉したい場合は『error()』にテーブルを渡します。
ok_example.lua
local ok, err = pcall(function()
error({ code = "E001", message = "invalid input" })
end)
print(type(err)) -- table
print(err.code) -- E001
print(err.message) -- invalid input
table E001 invalid input
よくあるミス2: error の level を指定せずライブラリ内部の行番号が出る
『error(msg)』のデフォルト(『level=1』)はエラーが発生した場所、つまりライブラリ関数内の行番号を報告します。呼び出し元のミスを伝えたい場合は『level=2』を指定して呼び出し元の行番号を示します。
ng_example2.lua
local function validate(x)
if type(x) ~= "number" then
error("number expected") -- level=1: ライブラリ内部の行番号が出る
end
end
validate("hello")
lua: ng_example2.lua:3: number expected
『level=2』を指定すると呼び出し元の行番号が報告されます。
ok_example2.lua
local function validate(x)
if type(x) ~= "number" then
error("number expected", 2) -- level=2: 呼び出し元の行番号が出る
end
end
validate("hello")
lua: ok_example2.lua:7: number expected
概要
Lua の『error()』は任意の値(文字列・テーブルなど)を引数にとってプログラムの実行を中断します。第二引数『level』でエラー位置情報をどの呼び出し段階に帰属させるかを制御でき、ライブラリ関数の内部から呼び出すときは『level=2』を指定することで呼び出し元の行番号をエラーに含められます。『assert()』は第一引数が『false』または『nil』のときにエラーを発生させるシンプルな関数で、入力値の検証や関数の前提条件チェックに使います。成功時は引数をそのまま返すため、local f = assert(io.open(path), "ファイルが開けません") のように代入と検証を一行で書けます。エラーを捕捉するには『pcall()』を使います。『xpcall()』はエラーハンドラ関数を受け取り、『debug.traceback()』と組み合わせることでスタックトレースを取得できます。エラーオブジェクトをテーブルにすると『code』や『message』などの構造化された情報をエラーに持たせられ、エラーハンドリングのコードを読みやすくできます。エラー処理の全体像については pcall / xpcall も合わせて確認してください。
記事の間違いや著作権の侵害等ございましたらお手数ですがこちらまでご連絡頂ければ幸いです。