البرمجة وتطوير البرمجيات

دليل عملي لإدارة الأخطاء في Go: من الإرجاع والتغليف إلى الاسترداد

يشرح دليل JetBrains فلسفة Go في التعامل مع الأخطاء باعتبارها قيماً تعود ضمن مسار التنفيذ الطبيعي، ويستعرض تقنيات الإرجاع والتغليف والفحص والدمج والاسترداد. كما يحدد متى ينبغي استخدام panic وrecover، وكيفية تجنب تجاهل الأخطاء أو فقدان سياقها أثناء انتقالها بين الدوال.

02 سبتمبر 2026
5 دقائق قراءة
136 قراءة
certi.news Editorial Team
دليل عملي لإدارة الأخطاء في Go: من الإرجاع والتغليف إلى الاسترداد

تعتمد Go على تصور مختلف نسبياً للأخطاء مقارنة بلغات مثل Java وC++ وJavaScript وPython: الخطأ في Go قيمة من النوع المدمج error، وتعيده الدالة عادة إلى المستدعي إلى جانب قيم الإرجاع الأخرى. وبذلك يصبح فحص الخطأ والتعامل معه جزءاً ظاهراً من مسار البرنامج، بدلاً من نقله إلى آلية استثناءات منفصلة.

يقدم دليل JetBrains، الذي كتب أصلاً بواسطة المساهم المجتمعي Christoph Berger ثم نُقل إلى مدونة JetBrains Go، مجموعة من التقنيات والممارسات للتعامل مع أخطاء الإدخال والإخراج والشبكات والتحقق من البيانات وغيرها. وقد جرى تحديثه في أغسطس 2026 لمواكبة أحدث تغييرات لغة Go.

الإرجاع هو نقطة البداية

عندما لا تملك الدالة سياقاً كافياً لمعالجة المشكلة، فعليها إعادة الخطأ إلى الدالة المستدعية. وتضع Go قيمة الخطأ عادة في آخر قائمة الإرجاع؛ إذ تعيد دالة مثل ReadFile() محتوى الملف وخطأً تكون قيمته nil عند النجاح أو غير ذلك عند الفشل.

يوضح الدليل أن على المستدعي اختبار الخطأ فوراً بدلاً من تجاهله. وإذا كانت الدالة قد فتحت مورداً مثل ملف أو اتصالاً شبكياً، فينبغي استخدام defer لتنظيفه عند الخروج، لكن بعد التأكد من نجاح عملية الفتح أولاً، لأن محاولة إغلاق مورد غير صالح قد تؤدي إلى مشكلة إضافية.

إضافة السياق دون إتلاف بنية الخطأ

قد ينتقل الخطأ عبر سلسلة من الدوال قبل أن تتم معالجته أو تسجيله. وخلال هذا الانتقال، يمكن لكل دالة إضافة معلومات مفيدة مثل العملية التي فشلت أو المسار الذي كانت تعالجه. لكن تحويل الخطأ إلى نص عبر دمج err.Error() يفقد السلسلة والبنية النوعية للخطأ.

الأسلوب الصحيح هو استخدام fmt.Errorf() مع المعامل %w لتغليف الخطأ مع الاحتفاظ بالخطأ الأصلي. ويتيح ذلك لاحقاً استخدام errors.Unwrap() للوصول إلى طبقة واحدة، أو استخدام errors.Is() وerrors.As() لاختبار أخطاء محددة داخل سلسلة التغليف. وتضيف Go 1.26 الدالة العامة errors.AsType() كبديل آمن نوعياً لـ As()؛ فهي تعيد الخطأ المطابق وقيمة منطقية، وتسمح للمترجم باكتشاف أخطاء النوع التي قد تظهر وقت التشغيل مع الأسلوب الأقدم.

الأخطاء المتعددة والسياق الملغى

لا تكون الأخطاء دائماً سلسلة خطية. فعند معالجة مجموعة ملفات مثلاً، قد تنجح بعض العمليات وتفشل أخرى. تقدم الحزمة القياسية errors.Join()، المتاحة منذ Go 1.20، لجمع عدة أخطاء في قيمة واحدة مع الاحتفاظ بالمحتوى الذي تمت معالجته بنجاح.

ينبه الدليل إلى أن errors.Unwrap() يعيد قيمة واحدة، ولذلك يعيد nil عند التعامل مع خطأ مدمج. وللوصول إلى الأخطاء المجمعة، يجب التحقق من أن القيمة تنفذ الواجهة التي توفر Unwrap() []error. كما يمكن منذ Go 1.20 استخدام context.WithCancelCause() لربط إلغاء السياق بسبب مخصص، ثم استرداد ذلك السبب عبر context.Cause(ctx) بدلاً من الاكتفاء بالقيمة العامة context.Canceled.

متى يكون panic مناسباً؟

لا ينبغي أن تكون panic وrecover بديلاً اعتيادياً عن فحص الأخطاء. فالأخطاء المتوقعة، مثل مدخلات المستخدم غير الصالحة أو الملفات المفقودة أو مهلات الشبكة، يجب التعامل معها وإعادتها عبر مسار الإرجاع المعتاد.

يصبح الذعر مناسباً عندما تكون المشكلة غير متوقعة ولا توجد معالجة ذات معنى لها، مثل فشل تجميع تعبير نمطي ثابت كان ينبغي التحقق من صحته مسبقاً. وفي حالات خوادم HTTP، قد يستخدم التطبيق recover() داخل دالة مؤجلة لمنع انهيار معالجة الطلب الحالي من التأثير في الطلبات الأخرى، حيثما كان ذلك ممكناً. أما حالات مثل نفاد الذاكرة فقد لا تترك للتطبيق خياراً عملياً للاستمرار.

ما الذي يهم عملياً للمطورين؟

  • لا تتجاهل الأخطاء: إسناد قيمة الخطأ إلى المعرف الفارغ أو إسقاط قيمة الإرجاع الوحيدة قد يؤخر اكتشاف المشكلة ويجعل آثارها اللاحقة أصعب في التشخيص.
  • سجّل في المكان المناسب: على الدالة أن تعالج الخطأ أو تعيده إلى المستدعي. ويوصي الدليل بأن تتجنب المكتبات التسجيل من تلقاء نفسها، لأن مستخدميها قد يفضلون سجلات أو وجهات إخراج مختلفة.
  • استخدم أنواعاً مناسبة: يمكن لأنواع الأخطاء المخصصة حمل معلومات إضافية، كما تفعل fs.PathError التي توفر العملية والمسار والخطأ الداخلي.
  • ميّز أخطاء الشبكة: يتيح net.OpError فحص طبيعة فشل الاتصال، وقد يسمح الخطأ المؤقت بإعادة المحاولة أو استخدام استراتيجية مثل التراجع الأسي.
  • استفد من عدد البايتات في عمليات الإدخال والإخراج: تعيد واجهة io.Reader عدد البايتات التي عولجت مع الخطأ، ما قد يساعد على استئناف العملية بدلاً من إعادة كل البيانات.
  • انتبه إلى io.EOF: تشير هذه القيمة إلى نهاية ناجحة لتيار القراءة وفق دلالات الحزمة، وليست بالضرورة فشلاً يحتاج إلى تسجيل كخطأ عادي.
  • لا تستخدم log.Fatal() بلا حساب: فهي تستدعي os.Exit()، ما يؤدي إلى تجاوز الدوال المؤجلة، ولذلك يقترح الدليل حصر استخدامها أو استخدام os.Exit() في الدالة main().

الخلاصة التحريرية من certi.news هي أن وضوح مسار الخطأ جزء من تصميم البرنامج، وليس مجرد أسلوب كتابة مطول. فالتغليف المنظم والأنواع المناسبة والتسجيل المسؤول تجعل التشخيص قابلاً للتنفيذ، بينما يؤدي الاعتماد على panic أو الرسائل العامة أو إسقاط الأخطاء إلى إخفاء المعلومات التي يحتاجها المطور لاحقاً. وتظل المفاضلة العملية مرتبطة بسياق التطبيق: فخادم الطلبات قد يحتاج إلى استرداد محدود، في حين أن خطأ لا يمكن التعافي منه قد يستدعي الإنهاء وإعادة التشغيل.

مصدر الخبر
كيف أعددنا هذا الخبر؟

اعتمد الخبر على المصدر الأصلي الموضح أعلاه. قد نستخدم أدوات آلية للمساعدة في الاستخراج والتصنيف والصياغة، لكن النشر يخضع لقواعد تمنع المحتوى المكرر والقصير أو الروتيني منخفض القيمة. اقرأ سياستنا التحريرية.

c
كاتب المقال

certi.news Editorial Team

certi.news Editorial Team

The certi.news editorial team monitors technical sources and reconstructs news, verifying facts and context prior to publication.

ما الذي تحتاج معرفته

يعرض دليل JetBrains ممارسات إدارة الأخطاء في Go، بدءاً من إرجاع الخطأ وفحصه، مروراً بتغليفه والحفاظ على سياقه، ووصولاً إلى استخدام panic وrecover عند الحاجة فقط. يركز الدليل على عدم تجاهل الأخطاء، واختيار أنواع الأخطاء المناسبة، وترك قرار التسجيل للتطبيق المستدعي.

  • تعيد دوال Go الأخطاء عادةً ضمن قيم الإرجاع، وينبغي فحصها فوراً بدلاً من تجاهلها.
  • يُستخدم fmt.Errorf() مع %w لإضافة سياق مع الحفاظ على الخطأ الأصلي، ثم يمكن فحص السلسلة عبر errors.Is() وerrors.As().
  • تجمع errors.Join() عدة أخطاء في قيمة واحدة، بينما يتيح context.WithCancelCause() وcontext.Cause() ربط إلغاء السياق بسبب مخصص.
  • ينبغي حصر panic وrecover في الحالات غير المتوقعة أو سيناريوهات الاسترداد المحدودة، لا في الأخطاء المتوقعة مثل الملفات المفقودة أو مدخلات المستخدم غير الصالحة.
  • ينبغي تجنب تسجيل الأخطاء داخل المكتبات تلقائياً، والانتباه إلى دلالات io.EOF وسلوك log.Fatal() الذي يتجاوز الدوال المؤجلة.

أسئلة شائعة

متى ينبغي إعادة الخطأ إلى الدالة المستدعية؟

عندما لا تملك الدالة سياقاً كافياً لمعالجة المشكلة، فعليها إعادة الخطأ إلى المستدعي.

ما فائدة استخدام %w مع fmt.Errorf()؟

يضيف سياقاً إلى الخطأ مع الاحتفاظ بالخطأ الأصلي وسلسلة التغليف، ما يسمح لاحقاً باستخدام errors.Unwrap() أو errors.Is() أو errors.As().

متى يكون استخدام panic مناسباً؟

يكون مناسباً عندما تكون المشكلة غير متوقعة ولا توجد معالجة ذات معنى لها، وليس كبديل اعتيادي لفحص الأخطاء.

هل io.EOF يمثل فشلاً دائماً؟

لا. يشير io.EOF إلى نهاية ناجحة لتيار القراءة وفق دلالات الحزمة، وليس بالضرورة إلى فشل عادي ينبغي تسجيله كخطأ.

من نفس التصنيف

مقالات قد تهمك

عرض كل الأخبار