اقتراح: محاولة - المدمج في وظيفة التحقق من الخطأ

ملخص


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


إن تصميم try سهل التفسير ، وسهل التنفيذ ، وهذه الوظيفة غير متجانسة مع بنيات اللغات الأخرى ومتوافقة تمامًا مع الإصدارات السابقة. كما أنها قابلة للمد إذا أردنا ذلك في المستقبل.


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


مقدمة


في مؤتمر Gophercon الأخير في دنفر ، قدم فريق Go (Russ Cox ، Marcel van Lohuizen) بعض الأفكار الجديدة حول كيفية الحد من التعب اليدوي لمعالجة الأخطاء في Go ( تصميم المسودة ). منذ ذلك الحين تلقينا قدرا كبيرا من ردود الفعل.


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


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


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


  • اقترح PeterRK أول اقتراح خطي (معروف لنا) لاستخدام إنشاء check بدلاً من المشغل في رسالته الأجزاء الأساسية لمعالجة الأخطاء
  • منذ وقت ليس ببعيد ، اقترح Markus كلمتين رئيسيتين جديدتين ، must حمايةهما must بالإضافة إلى استخدام " defer التفاف الأخطاء" في # 31442
  • كما اقترح pjebs must بناء في # 32219

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


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


أخيرًا ، بعد نشر هذا الاقتراح ، علمنا أن Ryan Hileman قام بتطبيقه قبل خمس سنوات باستخدام أداة og rewriter واستخدمه بنجاح في مشاريع حقيقية. راجع ( https://news.ycombinator.com/item؟id=20101417 ).


المدمج في محاولة وظيفة


عرض


نقترح إضافة عنصر لغة جديد يشبه الوظيفة يسمى try ويطلق عليه توقيع


 func try(expr) (T1, T2, ... Tn) 

حيث تعني expr تعبيرًا عن معلمة إدخال (عادةً ما تكون استدعاء دالة) تقوم بإرجاع قيم n + 1 لأنواع T1, T2, ... Tn error للقيمة الأخيرة. إذا كانت قيمة expr هي قيمة فردية (n = 0) ، فيجب أن تكون هذه القيمة من النوع type وأن لا تُرجع try نتيجة. استدعاء try باستخدام تعبير لا يُرجع القيمة الأخيرة error type ينتج عنه error في الترجمة.


لا يمكن استخدام إنشاء try إلا في دالة تُرجع قيمة واحدة على الأقل ، وتكون قيمة الإرجاع الأخيرة الخاصة بها من النوع type error . استدعاء try في سياقات أخرى يؤدي إلى خطأ ترجمة.


استدعاء دالة مع الدالة f() كما في المثال


 x1, x2, … xn = try(f()) 

يؤدي إلى الكود التالي:


 t1, … tn, te := f() // t1, … tn,  ()   if te != nil { err = te //  te    error return //     } x1, … xn = t1, … tn //     //     

بمعنى آخر ، إذا كان نوع error الأخير الذي تم إرجاعه بواسطة expr هو nil ، try ببساطة إرجاع القيم n الأولى ، وإزالة nil النهائي.


إذا كانت القيمة الأخيرة التي تم إرجاعها بواسطة expr غير nil ، فعندئذٍ:


  • قيمة إرجاع error للدالة المضمّنة (في الرمز الكاذب أعلاه المسماة err ، على الرغم من أن هذا يمكن أن يكون أي معرف أو قيمة إرجاع غير مسماة) تتلقى قيمة الخطأ التي تم إرجاعها من expr
  • هناك خروج من وظيفة المغلف
  • إذا كانت الدالة المغلقة تحتوي على معلمات إرجاع إضافية ، تحتفظ هذه المعلمات بالقيم التي تم تضمينها فيها قبل استدعاء try .
  • إذا كانت الدالة المرفقة تحتوي على معلمات إرجاع غير محددة إضافية ، فسيتم إرجاع قيم الصفر المقابلة لها (وهو مطابق لحفظ قيم الصفر الأصلية التي تمت تهيئتها بها).

إذا try استخدام try في تعيينات متعددة ، كما في المثال أعلاه ، وخطأ غير صفري (يشار إليه فيما يلي بعدم الصفر - تقريبًا لكل.) تم الكشف ، لا يتم تنفيذ الواجب (حسب متغيرات المستخدم) ولا يتغير أي من المتغيرات على الجانب الأيسر من المهمة. بمعنى ، try تتصرف مثل استدعاء دالة: نتائجها متوفرة فقط إذا try إرجاع التحكم إلى المتصل (على عكس الحالة مع إرجاع من الدالة المرفقة). نتيجة لذلك ، إذا كانت المتغيرات على الجانب الأيسر من الواجب هي معلمات إرجاع ، فإن استخدام try سيؤدي إلى سلوك مختلف عن الكود النموذجي الذي يتم مواجهته الآن. على سبيل المثال ، إذا تم تسمية a,b, err بمعلمات الإرجاع الخاصة بالدالة المرفقة ، فإليك هذا الرمز:


 a, b, err = f() if err != nil { return } 

سيقوم دائمًا بتعيين قيم للمتغيرات a, b و err ، بغض النظر عما إذا كانت المكالمة إلى f() أرجعت خطأ أم لا. التحدي المعاكس


 a, b = try(f()) 

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


استخدام


يخبرك تعريف try بشكل صريح بكيفية استخدامها: يمكن الاستعاضة عن الكثير من التعبيرات التي تحقق من وجود أخطاء. على سبيل المثال:


 f, err := os.Open(filename) if err != nil { return …, err //       } 

يمكن تبسيطها ل


 f := try(os.Open(filename)) 

إذا لم تُرجع وظيفة الاتصال خطأً ، فلا يمكن استخدام try (انظر قسم المناقشة). في هذه الحالة ، يجب معالجة الخطأ على أي حال محليًا (نظرًا لعدم وجود إرجاع خطأ) ، وفي هذه الحالة ، if ظلت الآلية المناسبة للتحقق من الأخطاء.


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


اختبار ومحاولة


في إحدى محاولاتنا السابقة لكتابة مواصفات (انظر قسم التكرار التصميمي أدناه) ، تم تصميم try للذعر عند حدوث خطأ عند استخدامها داخل إحدى الوظائف دون حدوث خطأ في الإرجاع. سمح باستخدام try في اختبارات الوحدة بناءً على حزمة testing الخاصة بالمكتبة القياسية.


كأحد الخيارات ، من الممكن استخدام وظائف الاختبار مع التواقيع في حزمة testing


 func TestXxx(*testing.T) error func BenchmarkXxx(*testing.B) error 

من أجل السماح باستخدام try في الاختبارات. دالة اختبار تقوم بإرجاع خطأ غير صفري سوف تستدعي ضمنيًا t.Fatal(err) أو b.Fatal(err) . هذا تغيير مكتبة صغير يتجنب الحاجة إلى سلوكيات مختلفة (رجوع أو ذعر) try ، اعتمادًا على السياق.


أحد عيوب هذا النهج هو أن t.Fatal و b.Fatal لن يكونا قادرين على إرجاع رقم السطر الذي سقط عليه الاختبار. عيب آخر هو أنه يجب علينا تغيير الاختبارات بطريقة أو بأخرى. الحل لهذه المشكلة هو سؤال مفتوح. لا نقترح تغييرات محددة على حزمة testing في هذا المستند.


انظر أيضًا رقم 21111 ، مما يشير إلى السماح لوظائف المثال بإرجاع خطأ.


خطأ في التعامل


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


هذا الاقتراح يقلل من مشروع التصميم الأصلي إلى جوهره. إذا كان إثراء الخطأ أو الالتفاف مطلوبًا ، فهناك طريقتان: إرفاق بـ if err != nil { return err} ، أو "إعلان" معالج خطأ داخل تعبير defer :


 defer func() { if err != nil { //      -   err = … // /  } }() 

في هذا المثال ، err اسم معلمة الإرجاع error النوع للدالة المرفقة.


في الممارسة العملية ، نتخيل وظائف المساعد مثل


 func HandleErrorf(err *error, format string, args ...interface{}) { if *err != nil { *err = fmt.Errorf(format + ": %v", append(args, *err)...) } } 

أو شيء مشابه. يمكن أن تصبح حزمة fmt مكانًا طبيعيًا لهؤلاء المساعدين (توفر بالفعل fmt.Errorf ). باستخدام المساعدين ، سيتم تخفيض تعريف معالج الأخطاء في كثير من الحالات إلى سطر واحد. على سبيل المثال ، لإثراء الخطأ من وظيفة "نسخة" ، يمكنك الكتابة


 defer fmt.HandleErrorf(&err, "copy %s %s", src, dst) 

إذا كان fmt.HandleErrorf يضيف معلومات الخطأ ضمنيًا ، فمن السهل جدًا قراءة هذا البناء وله ميزة أنه يمكن تنفيذه دون إضافة عناصر جديدة في بناء جملة اللغة.


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


تأجيل الكفاءة


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


حالات خاصة go try(f), defer try(f)


تبدو بنية try كدالة ، ولهذا السبب ، من المتوقع استخدامه في أي مكان تكون فيه استدعاء دالة مقبولة. ومع ذلك ، إذا تم استخدام call try في عبارة go ، فستكون الأمور معقدة:


 go try(f()) 

هنا يتم تنفيذ f() عند تنفيذ تعبير go في goroutine الحالي ، يتم تمرير نتائج استدعاء f كوسيطات try ، والتي تبدأ في goroutine الجديد. إذا f خطأ غير صفري ، فمن المتوقع أن try العودة من الدالة المغلقة ؛ ومع ذلك ، لا توجد وظيفة (وليس هناك أي معلمة إرجاع من error type) ، لأن يتم تنفيذ الرمز في goroutine منفصلة. لهذا السبب ، نقترح تعطيل try في تعبير go .


الوضع مع


 defer try(f()) 

يبدو مشابهاً ، ولكن دلالات التأجيل تعني هنا أن تنفيذ try سوف يتأخر حتى يعود من الوظيفة المغلقة. كما كان من قبل ، f() تقييم f() عند defer التأجيل ، ويتم تمرير نتائجها إلى try المؤجلة.


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


أخيرًا ، مثل بقية الإنشاءات المضمّنة ، يمكن استخدام try فقط كمكالمة. لا يمكن استخدامه كدالة قيمة أو في تعبير تعيين متغير كما في f := try (تمامًا مثل f := print و f := new ممنوع).


المناقشة


تكرارات التصميم


فيما يلي مناقشة مختصرة للتصاميم السابقة التي أدت إلى الحد الأدنى الحالي من الاقتراح. نأمل أن يلقي هذا الضوء على قرارات التصميم المحددة.


استلهم تكرارنا الأول من هذه الجملة فكرتين من مقالة "الأجزاء الأساسية لمعالجة الأخطاء" ، وهي استخدام الوظيفة المدمجة بدلاً من المشغل ووظيفة Go المعتادة للتعامل مع الأخطاء بدلاً من إنشاء لغة جديدة. على عكس هذا المنشور ، كان لدى معالج الأخطاء لدينا خطأ في توقيع ثابت func(error) error لتبسيط الأمور. سوف يتم استدعاء معالج الأخطاء بواسطة الدالة try إذا كان هناك خطأ قبل أن try الخروج من الدالة المغلقة. هنا مثال:


 handler := func(err error) error { return fmt.Errorf("foo failed: %v", err) //   } f := try(os.Open(filename), handler) //      

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


في التكرار التالي ، تمت إزالة القدرة على اجتياز معالج أخطاء مخصص لصالح استخدام defer التفاف الأخطاء. هذا يبدو وكأنه نهج أفضل لأنه جعل معالجات الأخطاء أكثر وضوحا في التعليمات البرمجية المصدر. ألغت هذه الخطوة أيضًا جميع المشكلات المتعلقة بالنقل الاختياري لوظائف المعالج ، ولكنها طلبت تسمية المعلمات التي تم إرجاعها بنوع error إذا كان الوصول مطلوبًا (قررنا أن هذا أمر طبيعي). علاوة على ذلك ، في محاولة لجعل try مفيدة ليس فقط داخل الوظائف التي تُرجع الأخطاء ، كان من الضروري جعل سلوك try حساسًا للسياق: إذا try استخدام try على مستوى الحزمة ، أو إذا تم استدعاؤها داخل دالة لا تُرجع خطأً ، try الذعر تلقائيًا عند اكتشاف خطأ. (وكتأثير جانبي ، وبسبب هذه الخاصية ، تم استدعاء بناء اللغة must بدلاً من try في هذه الجملة.) بدا السلوك الحساس للسياق try (أو must ) طبيعيًا ومفيدًا أيضًا: فهو سيؤدي إلى القضاء على العديد من الوظائف المعرفة من قبل المستخدم والمستخدمة في التعبيرات تهيئة متغيرات الحزمة. كما أنه فتح إمكانية استخدام الاختبارات في وحدة الاختبار مع حزمة testing .


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


لذلك ، في التكرار الحالي ، بدلاً من تضمين الوظيفة المضمنة الثانية ، قررنا إزالة الدلالات المزدوجة try ، وبالتالي السماح باستخدامها فقط في الوظائف التي تُرجع خطأً.


ميزات التصميم المقترح


هذا الاقتراح قصير جدًا وقد يبدو خطوة إلى الوراء مقارنة بمسودة العام الماضي. نعتقد أن الحلول المحددة لها ما يبررها:


  • أول الأشياء أولاً ، try تمامًا نفس دلالات بيان check المقترحة في الأصل بدون handle . هذا يؤكد دقة المسودة الأصلية في أحد الجوانب المهمة.


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


  • يتطلب استخدام دالة مضمّنة بدلاً من عامل تشغيل استخدام الأقواس. يجب أن نكتب try(f()) بدلاً من try f() . هذا هو السعر (الصغير) الذي يتعين علينا دفعه للتوافق مع الإصدارات السابقة مع المحللون الحاليون. ومع ذلك ، فإن هذا يجعل التصميم متوافقًا أيضًا مع الإصدارات المستقبلية: إذا قررنا أن تمرير وظيفة ما بطريقة خطأ أو إضافة معلمة إضافية try لهذا الغرض فكرة جيدة ، فإن إضافة وسيطة إضافية إلى استدعاء المكالمة ستكون تافهة.


  • كما اتضح فيما بعد ، فإن الحاجة إلى كتابة الأقواس لها مزاياها. try , , :



 info := try(try(os.Open(file)).Stat()) //   try info := try (try os.Open(file)).Stat() //  try   info := try (try (os.Open(file)).Stat()) //  try   

try , : try , .. try (receiver) .Stat ( os.Open ).


try , : os.Open(file) .. try ( , try os , , try try ).


, .. .


  • . , . , , , .


. , . defer , .


Go - , . , Go append . append , . , . , try .


, , Go : panic recover . error try .


, try , , — — , . Go:


  • , try
  • -

, , . if -.


تطبيق


:


  • Go.
  • try . , . .
  • go/types try . .
  • gccgo . ( , ).
  • .

- , . , . .


Robert Griesemer go/types , () cmd/compile . , Go 1.14, 1 2019.


, Ian Lance Taylor gccgo , .


"Go 2, !" , .


1 , , , Go 1.14 .


أمثلة


CopyFile :


 func CopyFile(src, dst string) (err error) { defer func() { if err != nil { err = fmt.Errorf("copy %s %s: %v", src, dst, err) } }() r := try(os.Open(src)) defer r.Close() w := try(os.Create(dst)) defer func() { w.Close() if err != nil { os.Remove(dst) //    “try”    } }() try(io.Copy(w, r)) try(w.Close()) return nil } 

, " ", defer :


 defer fmt.HandleErrorf(&err, "copy %s %s", src, dst) 

( defer -), defer , .


printSum


 func printSum(a, b string) error { x := try(strconv.Atoi(a)) y := try(strconv.Atoi(b)) fmt.Println("result:", x + y) return nil } 

:


 func printSum(a, b string) error { fmt.Println( "result:", try(strconv.Atoi(a)) + try(strconv.Atoi(b)), ) return nil } 

main :


 func localMain() error { hex := try(ioutil.ReadAll(os.Stdin)) data := try(parseHexdump(string(hex))) try(os.Stdout.Write(data)) return nil } func main() { if err := localMain(); err != nil { log.Fatal(err) } } 

- try , :


 n, err := src.Read(buf) if err == io.EOF { break } try(err) 


, .


: ?


: check handle , . , handle defer , handle .


: try ?


: try Go . - , . , . , " ". try , .. .


: try try?


: , check , must do . try , . try check (, ), - . . must ; try — . , Rust Swift try ( ). .


: ? Rust?


: Go ; , Go ( ; - ). , ? , . , , , (package, interface, if, append, recover, ...), , (struct, var, func, int, len, image, ..). Rust ? try — Go, , ( ) . , ? . , , (, ..) . . , .


: ( error) , defer , go doc. ?


: go doc , - ( _ ) , . , func f() (_ A, _ B, err error) go doc func f() (A, B, error) . , , , . , , . , , , -, (deferred) . Jonathan Geddes try() .


: defer ?


: defer . , , defer "" . . CL 171758 , defer 30%.


: ?


: , . , ( , ), . defer , . defer - https://golang.org/issue/29934 ( Go 2), .


: , try, error. , ?


: error ( ) , , nil . try . ( , . - ).


: Go , try ?


: try , try . super return -, try Go . try . .


: try , . ?


: try ; , . try ( ), . , if .


: , . try, defer . ?


: , . .


: try ( catch )?


: try — ("") , , ( ) . try ; . . "" . , . , try — . , , throw try-catch Go. , (, ), ( ) , . "" try-catch , . , , . Go . panic , .

Source: https://habr.com/ru/post/ar472758/


All Articles