0Pricing
Lua Academy · レッスン

pcallによる保護付き呼び出し

pcallでエラーを安全に捕捉し、戻り値を解釈します。

「pcallによる保護付き呼び出し」はCoddyKit上の無料Lua Academyレッスンです。 これはレッスン2/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはLua Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Lua Academyコースには全4レッスンが含まれています。

pcall の基本

pcall(f, ...) は、保護モードで引数 ... を付けて関数 f を呼び出します。f が成功すると、pcall は true に続けてすべての戻り値を返します。f がエラーになると、pcall は false に続けてエラーメッセージを返します。どちらの場合もプログラムは実行を継続します。

local function risky(x)
  if x < 0 then error("negative input: " .. x) end
  return math.sqrt(x)
end

local ok, result = pcall(risky, 9)
print(ok, result)    -- true  3.0

ok, result = pcall(risky, -1)
print(ok, result)    -- false  ...: negative input: -1

特定のエラーの捕捉

pcall の後でエラー値の型を確認し、エラーの種類ごとに異なる処理を行います。テーブルエラーには構造化されたデータがあり、文字列エラーにはメッセージがあります。常に両方のケースを処理してください。

local function op(errType)
  if errType == "table" then
    error({code=42, msg="structured error"})
  else
    error("plain string error")
  end
end

local ok, err = pcall(op, "table")
if not ok then
  if type(err) == "table" then
    print("Code:", err.code, "Msg:", err.msg)
  else
    print("String error:", err)
  end
end

成功時の複数戻り値

成功時、pcall は最初の true に続けて、保護された関数のすべての戻り値を返します。複数の変数で受け取ってください。

local function compute(a, b)
  return a + b, a * b, a - b
end

local ok, sum, product, diff = pcall(compute, 6, 4)
if ok then
  print("sum:", sum, "product:", product, "diff:", diff)
  -- sum: 10  product: 24  diff: 2
end

pcall のネスト

pcall の呼び出しはネストできます。それぞれの pcall が独立した保護スコープを作成します。内側の pcall で発生したエラーは外側ではなく内側の pcall に捕捉されます。これにより、フォールバック戦略を実装できます。

local function tryPrimary()
  error("primary failed")
end

local function tryFallback()
  return "fallback result"
end

local ok, result = pcall(tryPrimary)
if not ok then
  print("Primary failed:", result)
  ok, result = pcall(tryFallback)
end

if ok then print("Got:", result) end  -- fallback result

メソッドでの pcall

pcall 経由でメソッド(コロン構文)を呼び出すには、関数とオブジェクトを最初の引数として渡します。pcall(obj.method, obj, args...) のようにするか、クロージャーでラップします。

local obj = {
  x = 10,
  compute = function(self, n)
    if n == 0 then error("zero divisor") end
    return self.x / n
  end
}

-- Pass method + self explicitly
local ok, v = pcall(obj.compute, obj, 2)
print(ok, v)   -- true  5.0

-- Or use a closure
ok, v = pcall(function() return obj:compute(0) end)
print(ok, v)   -- false  ...zero divisor

IO の安全性に pcall を使う

IO 操作を pcall でラップし、予期しない OS エラーを捕捉します。一貫した結果とエラーのペアを返すことで、呼び出し側が失敗に適切に対処できるようにします。

local function safeRead(path)
  local ok, result = pcall(function()
    local f = assert(io.open(path, "r"))
    local content = f:read("a")
    f:close()
    return content
  end)
  if ok then return result
  else return nil, result
  end
end

local data, err = safeRead("config.txt")
if data then print("Read", #data, "bytes")
else print("Error:", err)
end

pcall によるリトライパターン

pcall を使ってエラーを捕捉し、不安定な操作(ネットワーク要求やファイル書き込み)を最大 N 回まで再試行します。実際のコードでは、再試行の間にスリープするか、コルーチンを使用します。

local function retry(fn, maxAttempts)
  local attempts = 0
  while attempts < maxAttempts do
    attempts = attempts + 1
    local ok, result = pcall(fn)
    if ok then
      print("Succeeded on attempt", attempts)
      return result
    end
    print("Attempt", attempts, "failed:", result)
  end
  error("all " .. maxAttempts .. " attempts failed")
end

local n = 0
retry(function()
  n = n + 1
  if n < 3 then error("not ready") end
  return "done"
end, 5)

pcall の結果をテーブルに保存する

ループ内で pcall を使用する場合は、後で分析できるように結果をテーブルに保存します。最初の失敗で停止せず、すべての項目を処理してエラーを収集したいバッチ処理で役立ちます。

local jobs = {10, -5, 25, 0, 16}
local results = {}

for _, n in ipairs(jobs) do
  local ok, v = pcall(function()
    if n < 0 then error("negative") end
    return math.sqrt(n)
  end)
  results[#results+1] = {input=n, ok=ok, value=v}
end

for _, r in ipairs(results) do
  if r.ok then
    print(r.input, "->", string.format("%.3f", r.value))
  else
    print(r.input, "-> ERROR:", r.value)
  end
end

pcall と xpcall の比較

pcall はエラーを捕捉しますが、得られるのはエラー値だけです。xpcall ではメッセージハンドラー関数を指定でき、スタックがまだ保持されている間に実行されるため、スタックトレースを取得できます。トレースバックが必要な場合は xpcall を選択してください。

-- pcall: simple, no traceback
local ok1, err1 = pcall(function()
  error("simple error")
end)
print(err1)   -- only the error message

-- xpcall: with traceback handler
local ok2, err2 = xpcall(
  function() error("traced error") end,
  function(e) return debug.traceback(e, 2) end
)
print(err2)   -- full stack trace

pcall の戻り値規約

pcall を基盤として一貫した API を設計します。成功時は常に value を返し、失敗時は nil, errorMessage を返します。関数をこの方式で構成すれば、呼び出し側は常に何が返るかを把握できます。

local function loadJSON(path)
  local ok, result = pcall(function()
    local f = assert(io.open(path, "r"))
    local text = f:read("a")
    f:close()
    -- Fake JSON parse for demo
    return {data = text, path = path}
  end)
  if ok then return result
  else return nil, result
  end
end

local data, err = loadJSON("data.json")
if data then print("Loaded:", data.path)
else print("Failed:", err)
end

理解度チェック

保護された関数が成功した場合、pcall は何を返しますか?

まとめ: pcall

まとめ:

  • pcall(f, ...) → true, results... または false, err
  • 最初の戻り値を確認して成功か失敗かを判断します
  • 成功時は true の後に複数の戻り値が続きます
  • pcall のネスト: それぞれが独立して自身のエラーを捕捉します
  • リトライパターン: 不安定な操作に対して pcall を使ったループを実行します
  • スタックトレースが必要な場合は xpcall を使用します

よくある質問

「pcallによる保護付き呼び出し」レッスンは無料ですか?

はい。「pcallによる保護付き呼び出し」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Lua Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Lua Academyコースには全4レッスンが含まれています。

「pcallによる保護付き呼び出し」で何を学びますか?

pcallでエラーを安全に捕捉し、戻り値を解釈します。 ブラウザで直接実行するハンズオンコードでLua Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Lua Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのLua Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン2/4です。

「pcallによる保護付き呼び出し」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このLua Academyレッスンでコードを書いて実行できますか?

はい。すべてのLua Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. error()関数
  2. pcallによる保護付き呼び出し
  3. xpcallとメッセージハンドラー
  4. 構造化エラーオブジェクト
← Lua Academyに戻る