0Pricing
Lua Academy · Lezione

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:42

Controllo 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
end

Gerarchia 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"))     -- false

Racchiudere 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))
end

Codici 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 denied

Errore 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)
end

Stampa 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
end

Assert 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) end

Verifica 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 __tostring per 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

  1. La funzione error()
  2. Chiamate protette con pcall
  3. xpcall e gestori dei messaggi
  4. Oggetti di errore strutturati
← Torna a Lua Academy