Rakenteiset virheoliot
Välittäkää taulukoita virheolioina tyypin ja kontekstin välittämistä varten.
Rakenteiset virheoliot on ilmainen Lua Academy-oppitunti CoddyKitissä. Tämä on oppitunti 4/4. Voit lukea tästä oppimispolusta kokonaan mitkä tahansa 3 oppituntia ilmaiseksi — sen jälkeen CoddyKit PRO avaa kaikki oppitunnit sekä käytännön harjoittelun sisäänrakennetulla koodieditorilla ja ympäri vuorokauden toimivalla tekoälytuutorilla. Oppitunti kuuluu Lua Academy-oppimispolkuun, ja edistymisesi synkronoituu verkon ja CoddyKit-sovelluksen välillä. Lua Academy-kurssilla on yhteensä 4 oppituntia.
Miksi rakenteisia virheitä?
Pelkkien merkkijonomuotoisten virheiden ohjelmallinen käsittely on hankalaa. Rakenteiset virheoliot (taulut) sisältävät tyyppitiedot ja kontekstidataa, ja kutsujat voivat tutkia niitä ja toimia niiden perusteella. Näin virheiden perusteella voidaan ohjata suoritus ilman merkkijonojen jäsentämistä.
-- 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"
})Virheen muodostimen malli
Luo jokaiselle virhetyypille virhetehtaan funktio. Tehdas muodostaa taulun, jossa on yhdenmukaiset kentät: tyyppi, viesti ja olennainen konteksti. __tostring-metametodi saa virheen tulostumaan selkeässä muodossa.
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:42Virheolioiden tyypin tarkistaminen
Kun olette ottaneet virheen kiinni, tarkistakaa, onko se taulu, jolla on tunnettu tyyppikenttä. Näin voitte valita eri palautumisstrategian virhetyypin perusteella ilman merkkijonojen hahmontunnistusta.
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
endVirhehierarkia
Voitte jäljitellä virhehierarkiaa tarkistamalla is_a-kenttiä tai käyttämällä metatauluja. Lapsivirhetyypit perivät ylätason virhetyypin kentät, ja koodi, joka ei tarvitse tarkempia tietoja, voi käsitellä niitä ylätason virheinä.
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")) -- falseVirheiden kääriminen
Kun otatte virheen kiinni ja heitätte sen uudelleen, käärikää alkuperäinen virhe kontekstin lisäämiseksi menettämättä alkuperäistä virhettä. Kääreellä on oma tyyppinsä, ja se sisältää alkuperäisen virheen aiheuttajana.
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))
endVirhekoodit ja virhetyypit
Kaksi yleistä käytäntöä ovat virhekoodit (numeeriset, kuten HTTP-tilakoodit) ja virhetyyppimerkkijonot (semanttiset nimet). Virhekoodeja on helppo verrata numeerisesti, kun taas tyyppimerkkijonot kuvaavat itse itseään. Monet järjestelmät käyttävät molempia.
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"))Virhepino (aiheuttajien ketju)
Kun virheen aiheuttaa toinen virhe, ketjuttakaa ne. Näin saatte kokonaiskuvan siitä, mikä meni vikaan sovelluksen kullakin tasolla. Purkakaa ketju, kun haluatte kirjata tai näyttää virheiden koko tapahtumaketjun.
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 deniedVirheet callback-kontekstissa
Kun virheitä tapahtuu callback-funktioissa, kuten tapahtumankäsittelijöissä tai iteraattoreissa, ne välittyvät callback-funktion kutsujalle. Ottakaa ne kiinni pcall-kutsulla ja raportoikaa samalla konteksti siitä, mikä callback epäonnistui.
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)
endVirheen tietojen tulostaminen
Apufunktio, joka tulostaa virheolion rakenteisessa ja luettavassa muodossa ja käsittelee sekä merkkijono- että taulumuotoiset virheet. Tämä on hyödyllistä sovelluksen rajoilla, joissa virheet kirjataan tai näytetään käyttäjille.
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
endRakenteiset virheet assert-tarkistuksissa
Luo assertT (tyypitettyjä virheitä käyttävä assert), joka nostaa pelkän merkkijonon sijaan rakenteisen virheen. Näin kutsujien on helppo testata tiettyjä virhetyyppejä.
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) endPikakertaus
Mikä on taulun välittämisen tärkein etu merkkijonon välittämiseen verrattuna funktiolle error()?
Kertaus: rakenteiset virheet
Yhteenveto:
- Välittäkää tauluja funktiolle
error(), jotta virheet ovat rakenteisia ja tutkittavissa - Sisällyttäkää tyyppi, viesti ja olennaiset kontekstikentät
- Lisätkää
__tostringhelposti luettavaa tulostusta varten - Käärikää virheet kontekstin lisäämiseksi menettämättä aiheuttajaa
- Ohjatkaa käsittelyä tyypin perusteella: tarkistakaa
err.type, älkää merkkijonohahmoja
Opi Lua tekoälytuutorin avulla — ilmaiseksi
Kirjoita ja suorita oikeaa koodia selaimessa, saa välitöntä apua tekoälytuutorilta ympäri vuorokauden ja jatka siitä, mihin jäit, verkossa tai sovelluksessa.
- Kurssit
- 40
- Oppitunnit
- 159
Usein kysytyt kysymykset
Onko oppitunti ”Rakenteiset virheoliot” ilmainen?
Kyllä — voit lukea täällä verkossa kokonaan ilmaiseksi mitkä tahansa Lua Academy-oppimispolun 3 oppituntia, myös oppitunnin “Rakenteiset virheoliot”. Sen jälkeen CoddyKit PRO avaa kaikki oppitunnit sekä interaktiiviset harjoitukset sisäänrakennetulla koodieditorilla ja ympäri vuorokauden toimivalla tekoälytuutorilla. Lua Academy-kurssilla on yhteensä 4 oppituntia.
Mitä opin oppitunnilla ”Rakenteiset virheoliot”?
Välittäkää taulukoita virheolioina tyypin ja kontekstin välittämistä varten. Harjoittelet Lua Academy-aihetta koodilla, jonka suoritat suoraan selaimessa. Ympäri vuorokauden käytettävissä oleva tekoälytuutori vastaa kysymyksiisi oppitunnin aikana.
Tarvitsenko kokemusta aloittaakseni Lua Academy-opiskelun?
Aiempi kokemus ei ole tarpeen. CoddyKitin Lua Academy-oppimispolku sopii vasta-alkajista edistyneisiin, joten voit aloittaa tästä tai alusta ja edetä omaan tahtiisi. Tämä on oppitunti 4/4.
Kuinka kauan ”Rakenteiset virheoliot”-oppitunnin suorittaminen kestää?
Useimmat CoddyKitin oppitunnit kestävät noin 5–10 minuuttia. Jokainen oppitunti on lyhyt ja interaktiivinen, joten edistyt tasaisesti ja voit jatkaa siitä, mihin jäit – sekä verkossa että sovelluksessa.
Voinko kirjoittaa ja suorittaa koodia tällä Lua Academy-oppitunnilla?
Kyllä. Jokainen Lua Academy-oppitunti sisältää sisäänrakennetun koodieditorin, joten voit kirjoittaa ja suorittaa oikeaa koodia suoraan selaimessa ja saada välitöntä palautetta tekoälyltä – paikallista asennusta ei tarvita.
Kaikki tämän kurssin oppitunnit
- error()-funktio
- Suojatut kutsut pcall-funktiolla
- xpcall ja viestinkäsittelijät
- Rakenteiset virheoliot