معالجة الأخطاء في Go: try/catch والتقنيات المتقدمة

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

معالجة الأخطاء في Go: try/catch وما بعدها: المستوى المتقدم



المطورون القادمون إلى Go من لغات تحتوي على استثناءات (Java، Python، C#) غالباً ما يتساءلون: "أين try/catch هنا؟". الإجابة بسيطة: لا توجد استثناءات في Go بالمعنى التقليدي. بدلاً من ذلك، تقدم اللغة نموذجاً صريحاً وقابلاً للتنبؤ وفعالاً لمعالجة الأخطاء. قد يبدو هذا للوهلة الأولى خطوة إلى الوراء، لكنه في الواقع أحد المبادئ الأساسية لفلسفة Go - البساطة والموثوقية. في هذه المقالة، لن نشرح فقط كيفية استبدال try/catch، بل سنتناول التقنيات المتقدمة للعمل مع الأخطاء: من الأنواع المخصصة إلى التغليف والذعر.



لماذا لا يوجد try/catch في Go؟



غياب try/catch هو قرار معماري متعمد. غالباً ما تؤدي الاستثناءات إلى تدفقات تنفيذ ضمنية يصعب تتبعها. يمكن للبرنامج أن "ينفجر" في أي مكان إذا نسيت معالجة استثناء. يقدم Go نهجاً مختلفاً: الخطأ هو مجرد قيمة (error is a value).



هذا يعني أن الدالة التي قد تفشل تعيد قيمة خطأ كجزء من نتيجتها. المبرمج ملزم بالتحقق من هذه القيمة. هذا النهج يجعل تدفق الأخطاء مرئياً في الكود ويمنع المواقف التي يتم فيها "ابتلاع" الاستثناء أو معالجته بعيداً جداً عن مكان حدوثه.



// Idiom نموذجي في Gofunc ReadFile(path string) ([]byte, error) {    data, err := os.ReadFile(path)    if err != nil {        return nil, fmt.Errorf("تعذر قراءة الملف %s: %w", path, err)    }    return data, nil}

// الاستخدامdata, err := ReadFile("config.json")if err != nil { log.Fatal(err) // أو return err}


هذا هو البديل لـ try/catch. بدلاً من كتلة try { ... } catch (Exception e) { ... } نستخدم if err != nil. هذا لا يقل تعبيراً، لكنه أكثر وضوحاً بكثير.



تقنيات متقدمة: الأخطاء المخصصة والتغليف



1. أنواع الأخطاء المخصصة



النوع القياسي error هو واجهة بطريقة واحدة Error() string. لكن للمعالجة المتقدمة، غالباً ما تحتاج إلى المزيد: رمز الخطأ، طبيعة العطل المؤقتة، بيانات إضافية. أنشئ نوعك الخاص الذي ينفذ واجهة error.



type MyError struct {    Code    int    Message string    IsTemp  bool}

func (e *MyError) Error() string { return fmt.Sprintf("الرمز %d: %s (مؤقت: %t)", e.Code, e.Message, e.IsTemp)}

// الاستخدامfunc DoSomething() error { return &MyError{Code: 503, Message: "الخدمة غير متاحة", IsTemp: true}}

// التحققerr := DoSomething()var myErr *MyErrorif errors.As(err, &myErr) { if myErr.IsTemp { // إعادة المحاولة }}


نصيحة: استخدم errors.As للتحويل الآمن إلى نوعك، بدلاً من التحويل المباشر (type assertion)، لتجنب الذعر.



2. تغليف الأخطاء (Wrapping)



واحدة من أقوى الميزات في Go 1.13+ هي إمكانية تغليف الأخطاء باستخدام fmt.Errorf والتوجيه %w. هذا ينشئ سلسلة من الأخطاء يمكن فكها باستخدام errors.Is و errors.As.



func OpenConfig() error {    _, err := os.Open("config.yaml")    if err != nil {        // نغلف الخطأ، نضيف سياقاً        return fmt.Errorf("فتح الإعدادات: %w", err)    }    return nil}

func LoadApp() error { err := OpenConfig() if err != nil { // نغلف مرة أخرى return fmt.Errorf("تحميل التطبيق: %w", err) } return nil}

// في مكان ما في mainerr := LoadApp()if errors.Is(err, os.ErrNotExist) { fmt.Println("ملف الإعدادات غير موجود")}


لاحظ: errors.Is يمر عبر السلسلة بأكملها ويجد

المدونات

توصيات الكتب