0Pricing
Lua Academy · Урок

Защищённые вызовы с pcall

Безопасно перехватывайте ошибки с помощью pcall и анализируйте возвращаемые значения.

«Защищённые вызовы с pcall» — бесплатный урок Lua Academy на CoddyKit. Это урок 2 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения 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

Повторяйте нестабильную операцию (сетевой запрос, запись в файл) до N раз, используя pcall для перехвата ошибок. В реальном коде делайте паузу между попытками (или используйте сопрограммы).

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/7) и разблокировать остальной курс Lua Academy, подпишись на CoddyKit PRO. Курс Lua Academy содержит 4 уроков всего.

Чему я научусь в уроке «Защищённые вызовы с pcall»?

Безопасно перехватывайте ошибки с помощью pcall и анализируйте возвращаемые значения. Ты практикуешь Lua Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать Lua Academy?

Предыдущий опыт не требуется. Lua Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 2 из 4.

Сколько времени занимает урок «Защищённые вызовы с pcall»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке Lua Academy?

Да. Каждый урок Lua Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Функция error()
  2. Защищённые вызовы с pcall
  3. xpcall и обработчики сообщений
  4. Структурированные объекты ошибок
← Назад к Lua Academy