Oggetti di errore strutturati
Passi tabelle come oggetti di errore per comunicare tipo e contesto.
Oggetti di errore strutturati è una lezione Lua Academy gratuita su CoddyKit. Questa è la lezione 4 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento Lua Academy, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso Lua Academy include 4 lezioni in totale.
Perché usare errori strutturati?
Gli errori rappresentati da semplici stringhe sono difficili da gestire programmaticamente. Gli oggetti di errore strutturati (tabelle) contengono informazioni sul tipo e dati di contesto, e possono essere esaminati e gestiti da chi chiama la funzione. Questo consente di instradare gli errori senza analizzare stringhe.
-- 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"
})Schema del costruttore di errori
Crei una funzione factory per ogni tipo di errore. La factory costruisce una tabella con campi coerenti: type, message e qualsiasi contesto rilevante. Un metametodo __tostring consente di visualizzare l'errore in modo leggibile.
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:42Controllo del tipo degli oggetti di errore
Dopo aver intercettato un errore, controlli se è una tabella con un campo type noto. Questo consente di scegliere diverse strategie di recupero in base al tipo di errore, senza affidarsi al confronto con schemi di stringhe.
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
endGerarchia degli errori
Simuli una gerarchia degli errori controllando i campi is_a oppure usando le metatabelle. I tipi di errore figli ereditano i campi del tipo padre e possono essere trattati come il padre dal codice che non ha bisogno di dettagli specifici.
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")) -- falseRacchiudere gli errori
Quando intercetta e rilancia un errore, racchiuda l'errore originale per aggiungere contesto senza perderlo. L'involucro ha un proprio tipo e contiene l'errore originale come causa.
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))
endCodici di errore e tipi di errore
Esistono due convenzioni comuni: i codici di errore (numerici, come i codici di stato HTTP) e le stringhe dei tipi di errore (nomi semantici). I codici di errore sono facili da confrontare numericamente, mentre le stringhe dei tipi sono autoesplicative. Molti sistemi usano entrambi.
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"))Stack degli errori (catena delle cause)
Quando un errore è causato da un altro errore, li colleghi in una catena. In questo modo ottiene una visione completa di ciò che è andato storto in ogni livello dell'applicazione. Scorra la catena a ritroso per registrare o visualizzare la storia completa dell'errore.
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 deniedErrore nel contesto di una callback
Quando si verificano errori all'interno delle callback (gestori di eventi, iteratori), si propagano al chiamante della callback. Usi pcall per intercettarli e segnalarli con il contesto relativo alla callback che ha avuto esito negativo.
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)
endStampa dei dettagli dell'errore
Un helper che stampa un oggetto di errore in modo strutturato e leggibile, gestendo sia gli errori stringa sia quelli rappresentati da tabelle. È utile ai confini dell'applicazione, dove gli errori vengono registrati o mostrati agli utenti.
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
endAssert con errori strutturati
Crei un assertT (assert con errori tipizzati) che generi un errore strutturato anziché una semplice stringa. In questo modo è facile verificare nei chiamanti la presenza di tipi di errore specifici.
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) endVerifica rapida
Qual è il principale vantaggio di passare una tabella a error() invece di una stringa?
Riepilogo: errori strutturati
Riepilogo:
- Passi tabelle a
error()per ottenere errori strutturati e ispezionabili - Includa: type, message e i campi di contesto rilevanti
- Aggiunga
__tostringper un output leggibile - Racchiuda gli errori per aggiungere contesto senza perdere la causa
- Nei gestori, scelga il tipo controllando
err.type, non gli schemi delle stringhe
Domande Frequenti
La lezione «Oggetti di errore strutturati» è gratuita?
Sì — il testo completo di «Oggetti di errore strutturati» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso Lua Academy, passa a CoddyKit PRO. Il corso Lua Academy include 4 lezioni in totale.
Cosa imparerò in «Oggetti di errore strutturati»?
Passi tabelle come oggetti di errore per comunicare tipo e contesto. Eserciti Lua Academy con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.
Ho bisogno di esperienza per iniziare Lua Academy?
Non è richiesta alcuna esperienza precedente. Lua Academy su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 4 di 4.
Quanto tempo richiede la lezione «Oggetti di errore strutturati»?
La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.
Posso scrivere ed eseguire codice in questa lezione Lua Academy?
Sì. Ogni lezione Lua Academy include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.
Tutte le lezioni di questo corso
- La funzione error()
- Chiamate protette con pcall
- xpcall e gestori dei messaggi
- Oggetti di errore strutturati