Защищённые вызовы с 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 resultpcall с методами
Чтобы вызвать метод (с синтаксисом через двоеточие) с помощью 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
endpcall и 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 — локальная установка не требуется.
Все уроки этого курса
- Функция error()
- Защищённые вызовы с pcall
- xpcall и обработчики сообщений
- Структурированные объекты ошибок