كائنات الأخطاء المهيكلة
مرّر الجداول بوصفها كائنات أخطاء لتوضيح النوع والسياق
كائنات الأخطاء المهيكلة درس مجاني في Lua Academy على CoddyKit. هذا هو الدرس 4 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Lua Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Lua Academy 4 دروس في المجموع.
لماذا الأخطاء المنظّمة؟
يصعب التعامل مع الأخطاء النصية العادية برمجيًا. تحمل كائنات الأخطاء المنظّمة (الجداول) معلومات عن النوع وبيانات السياق، ويمكن للمستدعين فحصها واتخاذ الإجراءات بناءً عليها. يتيح ذلك توجيه التنفيذ استنادًا إلى الأخطاء من دون تحليل السلاسل النصية.
-- 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"
})نمط مُنشئ الأخطاء
أنشئ دالة مصنع لكل نوع من أنواع الأخطاء. ينشئ المصنع جدولًا يحتوي على حقول متسقة: النوع، والرسالة، وأي سياق ذي صلة. تجعل دالة metamethod المسماة __tostring طباعة الخطأ بصورة جيدة.
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التحقق من أنواع كائنات الأخطاء
بعد التقاط الخطأ، تحقق مما إذا كان جدولًا يحتوي على حقل نوع معروف. يتيح لك ذلك توجيه استراتيجيات الاسترداد المختلفة بناءً على نوع الخطأ، من دون الاعتماد على مطابقة الأنماط النصية.
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تدرّج الأخطاء
يمكن محاكاة تدرّج للأخطاء من خلال التحقق من حقول is_a أو باستخدام الجداول الوصفية. ترث أنواع الأخطاء الفرعية حقول نوع الخطأ الأب، ويمكن للشيفرة التي لا تحتاج إلى التفاصيل معاملتها بوصفها النوع الأب.
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تغليف الأخطاء
عند التقاط خطأ وإعادة رفعه، غلّف الخطأ الأصلي لإضافة السياق من دون فقدانه. يكون للتغليف نوعه الخاص، ويحمل الخطأ الأصلي بوصفه سببًا.
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رموز الأخطاء مقارنةً بأنواع الأخطاء
هناك اصطلاحان شائعان: رموز الأخطاء (رقمية، مثل رموز حالة HTTP) وسلاسل أنواع الأخطاء (أسماء دلالية). يسهل مقارنة رموز الأخطاء رقميًا، بينما توضّح سلاسل الأنواع معناها بذاتها. تستخدم أنظمة كثيرة كليهما.
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"))مكدس الأخطاء (سلسلة الأسباب)
عندما يكون سبب خطأ ما خطأً آخر، اربط الأخطاء معًا. يمنحك ذلك صورة كاملة لما حدث من خطأ في كل طبقة من طبقات التطبيق. فكّ سلسلة الأخطاء لتسجيل القصة الكاملة للخطأ أو عرضها.
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الخطأ في سياق الاستدعاء الراجع
عندما تحدث الأخطاء داخل الاستدعاءات الراجعة (مثل معالجات الأحداث والمكررات)، فإنها تنتشر إلى مستدعي الاستدعاء الراجع. استخدم pcall لالتقاطها والإبلاغ عنها مع سياق يوضح أي استدعاء راجع فشل.
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طباعة تفاصيل الخطأ
دالة مساعدة تطبع كائن الخطأ بطريقة منظّمة وسهلة القراءة، مع التعامل مع أخطاء السلاسل النصية والجداول معًا. يفيد ذلك عند حدود التطبيق حيث تُسجّل الأخطاء أو تُعرض على المستخدمين.
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استخدام التأكيد مع الأخطاء المنظّمة
أنشئ assertT (تأكيد باستخدام أخطاء ذات أنواع محددة) لرفع خطأ منظّم بدلًا من سلسلة نصية عادية. يسهّل ذلك اختبار أنواع أخطاء محددة لدى المستدعين.
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تحقق سريع
ما الميزة الأساسية لتمرير جدول إلى error() بدلًا من سلسلة نصية؟
مراجعة: الأخطاء المنظّمة
الملخص:
- مرّر الجداول إلى
error()للحصول على أخطاء منظّمة قابلة للفحص - ضمّن: النوع، والرسالة، وحقول السياق ذات الصلة
- أضف
__tostringللحصول على مخرجات سهلة القراءة - غلّف الأخطاء لإضافة السياق من دون فقدان السبب
- وجّه التنفيذ حسب النوع في المعالجات: تحقق من
err.typeبدلًا من الأنماط النصية
الأسئلة الشائعة
هل درس «كائنات الأخطاء المهيكلة» مجاني؟
نعم — نص درس «كائنات الأخطاء المهيكلة» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Lua Academy، انتقل إلى CoddyKit PRO. تتضمن دورة Lua Academy 4 دروس في المجموع.
ماذا ستتعلم في «كائنات الأخطاء المهيكلة»؟
مرّر الجداول بوصفها كائنات أخطاء لتوضيح النوع والسياق تتمرن على Lua Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.
هل أحتاج إلى خبرة سابقة لأبدأ Lua Academy؟
لا تُشترط خبرة سابقة. Lua Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 4 من أصل 4.
كم من الوقت يستغرق درس «كائنات الأخطاء المهيكلة»؟
معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.
هل يمكنني كتابة وتشغيل أكواد في درس Lua Academy هذا؟
نعم. كل درس في Lua Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.
جميع الدروس في هذه الدورة
- الدالة error()
- الاستدعاءات المحمية باستخدام pcall
- xpcall ومعالجات الرسائل
- كائنات الأخطاء المهيكلة