تغليف الأخطاء (fmt.Errorf)
جزء من قسم Object Oriented Programming في رحلة GO على Coddy — الدرس 54 من 107.
عندما تمر الأخطاء عبر طبقات متعددة من تطبيقك، تصبح معرفة مكان نشأة الخطأ أمراً بالغ الأهمية. تتيح لك دالة fmt.Errorf في لغة Go مع الرمز %w تغليف الأخطاء، مما يحافظ على الخطأ الأصلي مع إضافة سياق إضافي.
يؤدي تغليف الأخطاء (Error wrapping) إلى إنشاء سلسلة من الأخطاء. يمكن لكل طبقة إضافة معلومات حول ما كانت تحاول القيام به عند حدوث الخطأ:
func ReadConfig(filename string) error {
data, err := os.ReadFile(filename)
if err != nil {
return fmt.Errorf("reading config file: %w", err)
}
// معالجة البيانات...
return nil
}الفعل %w مميز—فهو يغلف الخطأ الأصلي داخل الخطأ الجديد. وهذا يختلف عن %v، الذي يقوم فقط بتحويل الخطأ إلى سلسلة نصية ويفقد هوية الخطأ الأصلي.
func LoadSettings() error {
err := ReadConfig("settings.json")
if err != nil {
return fmt.Errorf("loading settings: %w", err)
}
return nil
}عندما يفشل LoadSettings، تظهر رسالة الخطأ السلسلة الكاملة: "loading settings: reading config file: open settings.json: no such file or directory". تضيف كل طبقة سياقاً، مما يجعل تصحيح الأخطاء أسهل بكثير.
لا يزال الخطأ المغلف يحتوي على الخطأ الأصلي بداخله. في الدرس القادم، ستتعلم كيفية فك تغليف هذه الأخطاء والتحقق مما بداخلها باستخدام errors.Is() و errors.As().
التحدي
سهللنقم ببناء نظام معالجة طلبات يوضح تغليف الأخطاء (error wrapping) عبر طبقات متعددة. ستقوم بإنشاء سلسلة من الدوال حيث تضيف كل طبقة سياقاً للأخطاء، مما يسهل تتبع مكان حدوث المشكلات بالضبط.
ستقوم بتنظيم الكود الخاص بك عبر ملفين:
orders.go: قم بإنشاء منطق معالجة الطلبات مع طبقات متعددة تقوم بتغليف الأخطاء أثناء انتشارها للأعلى.قم بتنفيذ ثلاث دوال تشكل سلسلة معالجة:
ValidateItem(itemID string) error- تعيد خطأ برسالة"item not found"إذا كان itemID هو"INVALID"، وإلا تعيدnilProcessOrder(orderID, itemID string) error- تستدعيValidateItem. إذا أعادت خطأً، قم بتغليفه بالسياق:"processing order [orderID]: %w". وإلا تعيدnilSubmitOrder(customerName, orderID, itemID string) error- تستدعيProcessOrder. إذا أعادت خطأً، قم بتغليفه بالسياق:"submitting order for [customerName]: %w". وإلا تعيدnil
يجب أن تستخدم كل طبقة
fmt.Errorfمع الفعل%wلتغليف الخطأ القادم من الطبقة الأدنى، مما يبني سلسلة من السياق.main.go: اقرأ تفاصيل الطلب من المدخلات، واستدعِSubmitOrder، واعرض النتيجة. في حالة حدوث خطأ، اطبع سلسلة الأخطاء الكاملة. في حالة النجاح، اطبع رسالة تأكيد.
سيتم توفير المدخلات التالية:
- السطر 1: اسم العميل (Customer name)
- السطر 2: معرف الطلب (Order ID)
- السطر 3: معرف العنصر (Item ID)
اطبع النتيجة:
- في حالة حدوث خطأ:
Error: [full error chain] - في حالة النجاح:
Order [orderID] submitted successfully for [customerName]
على سبيل المثال، إذا كانت المدخلات Alice، و ORD-123، و INVALID، يجب أن يكون مخرجك:
Error: submitting order for Alice: processing order ORD-123: item not foundلاحظ كيف تظهر رسالة الخطأ السلسلة الكاملة — يمكنك تتبع المشكلة من محاولة الإرسال في المستوى الأعلى، مروراً بمعالجة الطلب، وصولاً إلى فشل التحقق الفعلي. أضافت كل طبقة سياقها الخاص باستخدام %w.
وإذا كانت المدخلات Bob، و ORD-456، و ITEM-001، يجب أن يكون مخرجك:
Order ORD-456 submitted successfully for Bobورقة مرجعية
استخدم fmt.Errorf مع الفعل %w لتغليف الأخطاء مع الحفاظ على الخطأ الأصلي:
func ReadConfig(filename string) error {
data, err := os.ReadFile(filename)
if err != nil {
return fmt.Errorf("reading config file: %w", err)
}
return nil
}يقوم الفعل %w بتغليف الخطأ الأصلي داخل الخطأ الجديد، على عكس %v الذي يقوم فقط بتحويل الخطأ إلى سلسلة نصية ويفقد هوية الخطأ الأصلي.
يؤدي تغليف الخطأ إلى إنشاء سلسلة حيث تضيف كل طبقة سياقاً:
func LoadSettings() error {
err := ReadConfig("settings.json")
if err != nil {
return fmt.Errorf("loading settings: %w", err)
}
return nil
}تظهر رسالة الخطأ الناتجة السلسلة الكاملة: "loading settings: reading config file: open settings.json: no such file or directory"
جرّب بنفسك
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
scanner := bufio.NewScanner(os.Stdin)
// قراءة اسم العميل
scanner.Scan()
customerName := scanner.Text()
// قراءة معرف الطلب
scanner.Scan()
orderID := scanner.Text()
// قراءة معرف العنصر
scanner.Scan()
itemID := scanner.Text()
// TODO: استدعاء SubmitOrder مع قيم الإدخال
// TODO: في حالة حدوث خطأ، اطبع: Error: [full error chain]
// TODO: في حالة النجاح، اطبع: Order [orderID] submitted successfully for [customerName]
_ = customerName
_ = orderID
_ = itemID
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس Object Oriented Programming
1أساسيات الـ OOP في Go
الملفات الخارجيةمساحة العمل والـ Modules في Goالـ Packages والـ Importsالأسماء المصدرة مقابل غير المصدرةمقدمة إلى الـ OOP في Goالـ Structs كـ Classesتعريف الـ Methods في الـ Structsالـ Pointer Receivers مقابل الـ Value Receiversتهيئة الـ Structدوال الـ Constructorمراجعة - آلة حاسبة بسيطة4الواجهات
مقدمة في الواجهاتالتنفيذ الضمنيالواجهة كعقدالواجهة الفارغة (any)تأكيد النوع (Type Assertion)تبديل النوع (Type Switch)تركيب الواجهاتواجهات Stringer و Errorمراجعة - حاسبة الأشكال7التغليف
الحقول المصدرة وغير المصدرةالتغليف على مستوى الحزمةدوال الـ Getter والـ Setterإخفاء المعلومات في Goملخص - سجلات الطلاب10الأنواع العامة (Go 1.18+)
مقدمة في الأنواع العامةمعاملات الأنواعقيود الأنواعالـ Structs العامةحل بديل للـ Methods العامةملخص - المجموعات العامة13أنماط التصميم - الجزء الأول
مقدمة في أنماط التصميمنمط Singletonنمط Factoryنمط Abstract Factoryنمط Observerنمط Strategy2تعمق في الأنواع و Structs
الأنواع الأساسية والمركبةتعريفات الأنواع المخصصةStruct TagsStructs مجهولةStructs متداخلةالقيم الصفرية والافتراضيةمراجعة - دفتر العناوين5التركيب بدلاً من الوراثة
لماذا لا تدعم Go الوراثةأساسيات تضمين الـ Structترقية الـ Methodتضمين عدة Structsالتضمين مقابل التجميعحجب الـ Methods المضمنةملخص - الهيكل الهرمي للموظفين8معالجة الأخطاء و OOP
واجهة errorأنواع الأخطاء المخصصةتغليف الأخطاء (fmt.Errorf)أخطاء Sentinelerrors.Is() و errors.As()Panic و Defer و Recoverملخص - File Parser11المكتبة القياسية والبرمجة كائنية التوجه (OOP)
io.Reader و io.Writerواجهة sort.Interfaceواجهة fmt.Stringerencoding/json مع Structsواجهة http.Handlerمراجعة - نماذج REST API14أنماط التصميم - الجزء الثاني
نمط الأمرنمط المحولنمط المزيننمط طريقة القالبنمط الحالةنمط التركيبMiddleware كنمط مزين3المؤشرات والذاكرة
أساسيات المؤشرات في Goالمؤشرات إلى الـ Structsالتمرير بالقيمة مقابل التمرير بالمرجعدالة ()newGarbage Collection في Goملخص - بناء Linked List6تعدد الأشكال في Go
تعدد الأشكال عبر InterfacesDuck Typing في Goقواعد استيفاء الـ Interfaceمجموعات متعددة الأشكالحقن التبعيةمراجعة - معالج الدفع9التزامن والبرمجة كائنية التوجه (OOP)
أساسيات الـ Goroutinesالقنوات (Channels) والاتصالالقنوات المخزنة (Buffered) مقابل غير المخزنةجملة Selectsync.Mutex و sync.RWMutexsync.WaitGroupتصميم الـ Structs الآمنة للخيوط (Thread-Safe)مراجعة - Worker Pool