مرحبا يا هابروفسك المواطنين!
بدأت دورة
Golang Developer بالفعل في OTUS اليوم
، ونحن نعتبر هذه مناسبة ممتازة لمشاركة منشور آخر مفيد حول هذا الموضوع. اليوم دعونا نتحدث عن نهج الذهاب للأخطاء. لنبدأ!

إتقان معالجة الأخطاء العملية في كود Go الخاص بك
هذا المنشور جزء من سلسلة Before Getting Started ، حيث نستكشف عالم Golang ، ونشارك النصائح والأفكار التي يجب أن تعرفها عند كتابة التعليمات البرمجية في Go حتى لا تضطر إلى ملء المطبات الخاصة بك.أفترض أن لديك بالفعل تجربة أساسية على الأقل مع Go ، ولكن إذا شعرت أنك واجهت في وقت ما مادة مناقشة غير مألوفة ، فلا تتردد في التوقف مؤقتًا ، واستكشاف الموضوع والعودة.
الآن بعد أن مسحنا طريقنا ، دعنا نذهب!
تعتبر طريقة Go للتعامل مع الأخطاء واحدة من أكثر الميزات إثارة للجدل وإساءة استخدامها. في هذه المقالة ، سوف تتعلم طريقة Go للأخطاء ، وتفهم كيف تعمل "تحت الغطاء". ستتعلم بعض الطرق المختلفة ، انظر إلى الكود المصدري Go والمكتبة القياسية لمعرفة كيفية معالجة الأخطاء وكيفية التعامل معها. سوف تتعلم لماذا تلعب تأكيدات الكتابة دورًا مهمًا في معالجتها ، وسوف ترى التغييرات القادمة في معالجة الأخطاء التي تخطط لإدخالها في Go 2.

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

إذا كانت وظيفتك قد تفشل لسبب ما ، فمن المحتمل أن تقوم بإرجاع نوع
error
المعلن عنها سابقًا. حسب الاصطلاح ، تشير إعادة الخطأ إلى المتصل بشأن المشكلة ، ولا يعتبر إرجاع الخطأ خطأً. وبالتالي ، ستجعل المتصل يفهم أن هناك مشكلة قد حدثت ، وعليه أن يتعامل معها: فمن يدعِّم وظيفتك ، فهو يعلم أنه يجب عليه عدم الاعتماد على النتيجة قبل التحقق من وجود خطأ. إذا لم يكن الخطأ صفريًا ، فهو ملزم بمراجعته ومعالجته (سجل ، أو أعد ، أو صُرف ، أو اتصل بنوع من آلية إعادة المحاولة / واضحة ، وما إلى ذلك).
(3 // معالجة الأخطاء
5 // تابع)هذه المقتطفات شائعة جدًا في Go ، ويعتبرها البعض رمزًا متداولًا. يعامل المحول البرمجي المتغيرات غير المستخدمة كأخطاء ترجمة ، لذلك إذا كنت لا تريد التحقق من الأخطاء ، فيجب عليك تعيينها
لمعرف فارغ . ولكن بغض النظر عن مدى ملاءمة ذلك ، لا ينبغي تجاهل الأخطاء.
(4 // تجاهل الأخطاء غير آمن ، ويجب ألا تعتمد على النتيجة قبل التحقق من الأخطاء)لا يمكن الوثوق بالنتيجة حتى التحقق من الأخطاءيؤدي الخطأ الذي تم إرجاعه مع النتائج ، إلى جانب نظام Go type الصارم ، إلى تعقيد عملية كتابة التعليمات البرمجية المميزة. يجب أن تفترض دائمًا أن قيمة دالة تالفة ، إلا إذا قمت بالتحقق من الخطأ الذي عادت إليه ، وبتخصيص الخطأ لمعرف فارغ ، فإنك تتجاهل بشكل صريح أن قيمة وظيفتك قد تكون تالفة.
المعرف الفارغ مظلم ومليء بالرعب.لدى Go
panic
recover
آليات ، والتي تم وصفها أيضًا في
منشور Go مفصل آخر . لكن ليس الغرض منها محاكاة الاستثناءات. وفقًا لـ Dave ،
"عندما تشعر بالذعر في Go ، تشعر بالذعر حقًا: هذه ليست مشكلة شخص آخر ، إنها بالفعل لعبة." أنها قاتلة وتؤدي إلى تعطل في البرنامج. توصل روب بايك إلى مقولة "لا داعي للذعر" التي تتحدث عن نفسها: ربما يجب عليك تجنب هذه الآليات وإرجاع الأخطاء بدلاً من ذلك.
"الأخطاء هي المعاني."
"لا تقم فقط بالتحقق من الأخطاء ، بل تعامل معها بأناقة".
"لا تقلق"
كل اقوال روب بايك
تحت غطاء محرك السيارة
واجهة خطأتحت الغطاء ، يكون نوع الخطأ عبارة عن
واجهة بسيطة ذات طريقة واحدة ، وإذا لم تكن على دراية بها ، فإنني أوصي بشدة بعرض
هذه المشاركة على مدونة Go الرسمية.
واجهة خطأ من المصدرارتكاب أخطائك الخاصة ليس بالأمر الصعب. هناك طرق متعددة لبنى المستخدم التي تقوم بتطبيق أسلوب
string
Error()
. أي بنية تنفذ هذه الطريقة الفردية تعتبر قيمة خطأ صالحة ويمكن إرجاعها على هذا النحو.
دعونا ننظر في عدد قليل من هذه النهج.
المدمج في errorString هيكل
يعد تطبيق
errorString
المدمج هو التطبيق الأكثر شيوعًا والواسع الانتشار لواجهة الخطأ. هذا هو أسهل تطبيق يمكنك التفكير فيه.

المصدر:
اذهب شفرة المصدريمكنك أن ترى التنفيذ المبسط
هنا . كل ما تفعله هو يحتوي على
string
، ويتم إرجاع هذه السلسلة بواسطة طريقة
Error
. يمكن تنسيق خطأ السلسلة هذا بواسطتنا على أساس بعض البيانات ، على سبيل المثال ، باستخدام
fmt.Sprintf
. لكن بخلاف هذا ، فإنه لا يحتوي على أي ميزات أخرى. إذا قمت بتطبيق
أخطاء. جديدة أو
fmt.Errorf ، فأنت قد
استخدمت بالفعل.
(13 // الإخراج :)محاولةgithub.com/pkg/errors
مثال بسيط آخر هو حزمة
pkg / error . حتى لا يتم الخلط بينه وبين حزمة
errors
المضمنة التي تعلمتها سابقًا ، توفر هذه الحزمة ميزات مهمة إضافية ، مثل التفاف الأخطاء ، والتوسع ، والتنسيق ، وتسجيل تتبع المكدس. يمكنك تثبيت الحزمة عن طريق تشغيل
go get github.com/pkg/errors
.

في الحالات التي تحتاج فيها إلى إرفاق تتبع المكدس أو معلومات تصحيح الأخطاء الضرورية
Errorf
، فإن استخدام
Errorf
New
أو
Errorf
لهذه الحزمة يوفر الأخطاء التي تمت كتابتها بالفعل إلى تتبع المكدس الخاص بك ، ويمكنك أيضًا إرفاق بيانات تعريف بسيطة باستخدامه قدرات التنسيق.
Errorf
بتنفيذ واجهة
fmt.Formatter ، أي يمكنك تنسيقها باستخدام الأحرف الرونية لحزمة
fmt
(
%s
،
%v
،
%+v
، إلخ).
(// 6 أو بديل)تقدم هذه الحزمة أيضًا
errors.Wrap
errors.Wrapf
. تضيف هذه الوظائف السياق إلى الخطأ باستخدام رسالة وتتبع مكدس في المكان الذي تم استدعاؤها. وبالتالي ، بدلاً من إرجاع الخطأ ببساطة ، يمكنك لفه مع السياق وبيانات تصحيح الأخطاء الهامة.

تدعم مغلفات الأخطاء عن طريق أخطاء أخرى طريقة
Cause() error
، والتي تُرجع خطأها الداخلي. بالإضافة إلى ذلك ، يمكن استخدامها مع
errors.Cause(err error) error
وظيفة
errors.Cause(err error) error
، والتي تستخرج الخطأ الداخلي الرئيسي في خطأ التفاف.
خطأ في التعامل
اكتب الموافقة
تلعب تأكيدات الكتابة دورًا مهمًا عند التعامل مع الأخطاء. ستستخدمها لاستخراج المعلومات من قيمة الواجهة ، وبما أن معالجة الأخطاء مرتبطة بتطبيقات المستخدم لواجهة
error
، فإن تطبيق عبارات
error
يعد أداة ملائمة للغاية.
بناء الجملة الخاص به هو نفسه لجميع أغراضه -
x.(T)
إذا كان
x
يحتوي على نوع واجهة.
x.(T)
إلى أن
x
ليست
nil
وأن القيمة المخزنة في
x
هي من النوع
T
في الأقسام القليلة التالية ، سننظر في طريقتين لاستخدام عبارات الكتابة - بنوع محدد
T
وبواجهة من النوع
T
(2 // بناء جملة الاختزال تخطي متغير منطقي موافق
3 // الذعر: تحويل الواجهة: الواجهة {} لا شيء ، وليس سلسلة
6 // بناء الجملة الممتد مع موافق منطقية
8 // لا داعي للذعر ، وبدلا من ذلك يضبط خطأ كاذب عندما يكون البيان خاطئ
9 // الآن يمكننا استخدام s بأمان كسلسلة)رمل: الذعر مع بناء جملة مختصرة ، بناء جملة ممتدة آمنةملاحظة بناء جملة إضافية: يمكن استخدام تأكيد النوع إما باستخدام بناء جملة مختصرة (والذي يتسم بالذعر عند بيان غير ناجح) أو مع بناء جملة ممتد (والذي يستخدم القيمة المنطقية موافق للإشارة إلى النجاح أو الفشل). أوصي دائمًا بالطول ممدودًا بدلاً من تقصيرها ، حيث إنني أفضل التحقق من المتغير "موافق" ، وليس التعامل مع الذعر.
نوع الموافقة T
يؤكد بيان النوع
x.(T)
بواجهة من النوع
T
أن
x
تنفذ واجهة
T
وبالتالي ، يمكنك ضمان تنفيذ قيمة الواجهة للواجهة ، وفقط إذا كان الأمر كذلك ، يمكنك استخدام أساليبها.
(5 ... // يدعي أن x ينفذ واجهة محلل
6 ... // هنا يمكننا بالفعل استخدام هذه الطريقة بأمان)لفهم كيف يمكن استخدام هذا ، دعونا نلقي نظرة على
pkg/errors
مرة أخرى. أنت تعرف بالفعل حزمة الخطأ هذه ، لذلك دعونا ندخل في
errors.Cause(err error) error
وظيفة
errors.Cause(err error) error
.
تتلقى هذه الوظيفة خطأ وتستخرج الخطأ الأعمق الذي تعاني منه (الخطأ الذي لم يعد بمثابة التفاف لخطأ آخر). قد يبدو هذا بدائيًا ، ولكن هناك العديد من الأشياء الرائعة التي يمكنك تعلمها من هذا التطبيق:

المصدر:
كجم / الأخطاءتتلقى الدالة قيمة الخطأ ، ولا يمكنها افتراض أن وسيطة
err
التي تتلقاها هي خطأ مجمّع (مدعوم من قبل طريقة
Cause
). لذلك ، قبل الاتصال بأسلوب
Cause
، تحتاج إلى التأكد من أنك تتعامل مع خطأ يقوم بتنفيذ هذه الطريقة. من خلال تنفيذ عبارة كتابة في كل تكرار من حلقة for ، يمكنك التأكد من أن متغير
cause
يدعم طريقة
Cause
، ويمكنك الاستمرار في استخراج الأخطاء الداخلية منه حتى تجد خطأ لا يحتوي على
Cause
.
من خلال إنشاء واجهة محلية بسيطة تحتوي فقط على تلك الأساليب التي تحتاج إليها ، وتطبيق التأكيد عليها ، يتم فصل الكود الخاص بك عن التبعيات الأخرى. لا يجب أن تكون الحجة التي تلقيتها بنية معروفة ، بل يجب أن تكون مجرد خطأ. سيقوم أي نوع بتنفيذ أساليب
Error
Cause
. وبالتالي ، إذا قمت بتطبيق طريقة
Cause
في نوع الخطأ الخاص بك ، يمكنك استخدام هذه الوظيفة معها دون تباطؤ.
ومع ذلك ، هناك عيب واحد صغير يجب مراعاته: واجهات العرض عرضة للتغيير ، لذلك يجب عليك الحفاظ على الرمز بعناية حتى لا تنتهك عباراتك. لا تنس تحديد واجهاتك التي تستخدمها ، لإبقائها نحيفة وأنيقة ، وستكون بخير.
أخيرًا ، إذا كنت بحاجة إلى طريقة واحدة فقط ، فمن الملائم في بعض الأحيان تقديم بيان على واجهة مجهولة تحتوي فقط على الطريقة التي تعتمد عليها ، أي
v, ok := x.(interface{ F() (int, error) })
. يمكن أن يساعد استخدام واجهات مجهولة على فصل الشفرة عن التبعيات المحتملة وحمايتها من التغييرات المحتملة في الواجهات.
اكتب T و Type Switch Approval
أتقدم بهذا القسم من خلال تقديم نموذجين مماثلين للتعامل مع الأخطاء ويعانيان من العديد من العيوب والفخاخ. هذا لا يعني أنها ليست شائعة. يمكن أن يكون كلاهما أدوات ملائمة في المشروعات الصغيرة ، لكنهما لا يتقاسمان حجمًا جيدًا.
الأول هو الإصدار الثاني من تأكيد النوع: يتم تنفيذ تأكيد من النوع
x.(T)
بنوع محدد
T
يدعي أن قيمة
x
هي من النوع
T
، أو يمكن تحويلها إلى النوع
T
(2 // يمكننا استخدام v كـ mypkg.SomeErrorType)آخر هو نمط
تبديل النمط. يجمع Type Switch بين بيان التبديل وبيان type باستخدام الكلمة الأساسية
type
المحجوزة. إنها شائعة بشكل خاص في معالجة الأخطاء ، حيث قد يكون من المفيد للغاية معرفة النوع الأساسي للخطأ المتغير.
(3 // المعالجة ...
5 // المعالجة ...)العيب الكبير في كلا النهجين هو أن كلاهما يؤدي إلى ربط الكود مع التبعيات الخاصة بهم. يجب أن يكون كلا المثالين على دراية ببنية
SomeErrorType
(التي يجب تصديرها بوضوح) ويجب أن تستورد حزمة
mypkg
.
في كلا الاتجاهين ، عند التعامل مع أخطائك ، يجب أن تكون على دراية بالنوع واستيراد الحزمة الخاصة به. يتفاقم الموقف عندما تتعامل مع الأخطاء في الأغلفة ، حيث قد يكون سبب الخطأ هو خطأ ناشئ عن تبعية داخلية لا تعرفها ولا يجب أن تعرفها.
(7 // المعالجة ...
9 // المعالجة ...)نوع التبديل يميز بين
*MyStruct
و
MyStruct
. لذلك ، إذا لم تكن متأكدًا مما إذا كنت تتعامل مع مؤشر أو مثيل فعلي للبنية ، فسيتعين عليك تقديم كلا الخيارين. علاوة على ذلك ، كما هو الحال في رموز التبديل العادية ، لا تفشل الحالات الموجودة في Type Switch ، ولكن على عكس Type Switch المعتادة ، يُحظر استخدام
fallthrough
في Type Switch ، لذلك يجب عليك استخدام فاصلة وتوفير كلا الخيارين ، وهو أمر يسهل نسيانه.

لتلخيص
هذا كل شئ! أنت الآن على دراية بالأخطاء ويجب أن تكون مستعدًا لإصلاح أي أخطاء قد يلقيها تطبيق Go (أو يعيدها فعليًا) إلى طريقك!
توفر كلتا حزم الأخطاء طرقًا بسيطة ولكنها مهمة للأخطاء في Go ، وإذا كانت تلبي احتياجاتك ، فهي خيار رائع. يمكنك بسهولة تنفيذ هياكل الأخطاء الخاصة بك والاستفادة من معالجة الأخطاء Go من خلال
pkg/errors
مع
pkg/errors
.
عند قياس أخطاء بسيطة ، يمكن أن يكون الاستخدام الصحيح لبيانات الكتابة أداة رائعة لمعالجة الأخطاء المختلفة. إما باستخدام Type Switch ، أو عن طريق التحقق من صحة سلوك الخطأ والتحقق من الواجهات التي ينفذها.
ما التالي؟
خطأ في معالجة في الذهاب الآن ذات الصلة للغاية. الآن بعد أن حصلت على الأساسيات ، فقد تتساءل عما ينتظرنا للتعامل مع أخطاء Go!
يولي الإصدار التالي من Go 2 الكثير من الاهتمام لهذا ، ويمكنك بالفعل إلقاء نظرة على
مسودة الإصدار . بالإضافة إلى ذلك ، خلال
dotGo 2019 ، أجرى Marcel van Lojuizen محادثة ممتازة حول موضوع لا يمكنني إلا أن أوصي به -
"قيم الخطأ GO 2 اليوم" .
من الواضح أن هناك العديد من الأساليب والنصائح والحيل ، ولا يمكنني تضمينها جميعًا في منشور واحد! على الرغم من هذا ، أتمنى أن تستمتع به ، وسأراك في الحلقة التالية
من قبل البدء !
والآن في انتظار تقليديا لتعليقاتكم.