أساسيات الأخطاء
Error Basics
أساسيات الأخطاء — Error Basics
في أغلب اللغات، الأخطاء تُعالج عبر الاستثناءات (exceptions) — تُرمى في مكان وتُلتقط في مكان آخر. Go اختارت طريقاً مختلفاً تماماً: الأخطاء قيم عادية.
هذا يعني أنك تتعامل مع الخطأ كما تتعامل مع أي قيمة — تفحصه، تمرره، تغلّفه، أو تتجاهله (وهذا خطأ!).
واجهة error
error هو نوع مُدمج في Go — واجهة بأسلوب واحد:
type error interface {
Error() string
}
أي نوع يُنفّذ Error() string يُعتبر خطأ. بهذه البساطة.
إنشاء أخطاء بسيطة
نمط if err != nil
هذا هو النمط الأساسي في Go — ستراه في كل مكان:
result, err := someFunction()
if err != nil {
// تعامل مع الخطأ — Handle the error
return err // أو log أو retry
}
// استخدم result — Use result
نعم، هذا يعني كتابة if err != nil كثيراً. مجتمع Go يرى أن هذا ميزة وليس عيب — لأنه يجبرك على التفكير في كل حالة خطأ بدلاً من تجاهلها.
تغليف الأخطاء — Error Wrapping مع %w
عندما تلتقط خطأ من دالة داخلية وتريد إضافة سياق، استخدم %w:
لاحظ كيف أن %w يحفظ الخطأ الأصلي داخل الخطأ الجديد — مما يسمح لنا بفحصه لاحقاً بـ errors.Is.
الفرق بين %v و %w
// %v — ينسخ الرسالة فقط، يفقد الخطأ الأصلي
fmt.Errorf("فشل: %v", err) // لا يمكن استخدام errors.Is بعدها
// %w — يغلّف الخطأ الأصلي، يحفظه
fmt.Errorf("فشل: %w", err) // يمكن استخدام errors.Is
القاعدة: استخدم %w عندما تريد أن يبقى الخطأ الأصلي قابلاً للفحص.
تجاهل الأخطاء — Don’t!
أحد أخطر الأخطاء في Go هو تجاهل القيمة المُرجعة:
// ❌ خطير — تجاهل الخطأ
result, _ := riskyFunction()
// ✅ صحيح — تعامل مع الخطأ
result, err := riskyFunction()
if err != nil {
log.Fatal(err)
}
تجاهل الخطأ مقبول فقط عندما تكون متأكداً تماماً أنه لن يحدث، أو أنك لا تهتم بالنتيجة (مثل fmt.Println التي نادراً ما تفشل).
أنماط شائعة
1. الإرجاع المبكر — Early return:
func processFile(path string) error {
data, err := readFile(path)
if err != nil {
return fmt.Errorf("قراءة الملف: %w", err)
}
result, err := parse(data)
if err != nil {
return fmt.Errorf("تحليل البيانات: %w", err)
}
return save(result)
}
2. التجميع — collecting errors: