Go: الاقتراح: اترك "إذا يخطئ! = لا شيء" بمفرده؟

تم إنشاؤها على ٢٨ يونيو ٢٠١٩  ·  314تعليقات  ·  مصدر: golang/go

يضيف اقتراح Go2 رقم 32437 بناء جملة جديدًا إلى اللغة لجعل لغة النمذجة if err != nil { return ... } أقل تعقيدًا.

هناك العديد من المقترحات البديلة: # 32804 و # 32811 لأن الاقتراح الأصلي غير محبوب عالميًا.

لطرح بديل آخر في المزيج: لماذا لا نحتفظ به كما هو ؟

لقد أحببت الطبيعة الصريحة لمركب if err != nil وبالتالي لا أفهم سبب حاجتنا إلى بناء جملة جديد لهذا الغرض. هل الأمر حقا بذلك السوء؟

FrozenDueToAge Proposal Proposal-Hold error-handling

التعليق الأكثر فائدة

يجب أن يكون هناك طريقة واحدة فقط لعمل شيء ما

ال 314 كومينتر

أنا أؤيد هذا. يعجبني حقًا كيف أن تزيين كل خطأ قبل إعادته يضيف وثائق يمكن قراءتها من قبل الإنسان إلى المصدر (عادةً ما نقوم بتنسيق أخطائنا على النحو التالي "لا يمكن [ما أفعله في سطور التعليمات البرمجية هذه]: [خطأ سابق]") وكذلك للمستخدمين قراءة الأخطاء.

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

الأخطاء بدون أي شكل من أشكال السياق أو التتبع (السلسلة المجردة "EOF") عديمة الفائدة تمامًا. أعتقد أن وجود اختصارات تسهل إرجاع الأخطاء المجردة سيجعل برامج Go تطبع الكثير من الأخطاء غير المجدية.

إذا كان هناك أي شيء ، فيجب علينا دفع ودعم أخطاء التزيين بالسياق ، ربما بقواعد طبيب بيطري ونُسالة جديدة.

أنا أيضا أحب التحقق من الخطأ بشكل صريح. try محير والعائد الضمني غريب.

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

إليك مثال لا أوافق عليه بالضرورة:

value, err := foo()
return err if err != nil

سيسمح هذا بمقاربة أقصر ولكن لا تزال صريحة. وسيسمح بإضافة سياق!

ومع ذلك ، فإن inline ifs هي شيء من روبي ولا تشعر بجويش شديد ، لكن هذا مجرد عصف ذهني. ربما نجد شيئًا آخر.


تحرير: أضفت اقتراحًا لهذا هنا: https://github.com/golang/go/issues/32860

يجب أن يكون هناك طريقة واحدة فقط لعمل شيء ما

[...] لماذا لا تحتفظ بها كما هي؟

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

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

كثيرا هذا.

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

لست متأكدًا مما إذا كان البناء الجديد سيحل هذا حقًا. إذا قمت بلف if err := nil في وظيفة مساعدة مثل هذه ، فقد يساعد ذلك قليلاً (عفواً يا صديقي Go):

func handleErr(err error, cb func(error)) {
        if err := nil {
                cb(err)
        }
}

لكن المشكلة التي تجعل هذه الوظيفة المساعدة أقل فائدة بشكل عام هي نظام الكتابة ، وهو موضوع مختلف.

أنا أؤيد هذا. if err != nil { return err } ليس جزءًا من أي كود في قاعدة الكود الخاصة بنا. لذلك فإن المحاولة "الماكرو" لا معنى لها على الإطلاق. نعيد فقط الأخطاء المغلفة ، مع رسالة تصف السياق.

إضافة السياق عبر التأجيل ليس منطقيًا أيضًا ، لأننا نريد إرجاع رسائل خطأ مختلفة لتمييز الأنواع المختلفة من الأخطاء. ومع ذلك ، قد يكون A try(fn(), "my error message: %w") مفيدًا. ولكن حتى ذلك الحين ، قد تظل البنية if err != nil مفضلة ، بسبب أطوال الأسطر الأقصر.

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

type Result<T> interface {
  Expect(err error) T
  OrElse(defaultValue T) T
}

func From<T>(value T, err error) Result<T> { ... }

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

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

قد لا يكون من المنطقي تغييرها ، لأن المشكلة الخاطئة تحاول حلها.

الكود الذي نعرفه ليس معالجة الأخطاء.

if err != nil {
  return err
}

هذا خطأ معالجة nil . لم يتم التعامل مع قيمة الخطأ في أي وقت من الأوقات.

إذا كنت سأعرض هذا بلغة مختلفة يا روبي.

begin
 some_method_that_raises_an_error
rescue => e # catch any exception
  retry e        # throw it up again
end

هذا ينقل نفس سلوك كود golang. عندما نكتشف حدوث استثناء ثم نعيده. نحن فقط نرميها فوق المكدس.

في golang ، نحن return ذلك.

أين تقع معالجة الخطأ الفعلية؟

لقد مررنا جميعًا بتجارب مماثلة لفشل هذا النمط. على سبيل المثال ، تلقي خطأ file not found ثم قضاء فترة طويلة من الوقت في تتبع _thrower_ الأصلي لهذا الخطأ.

هذا هو السبب في أنني أعتقد أن اقتراح try (وغيره) به أخطاء. ليس لدينا نمط جيد للتعامل مع الأخطاء فعليًا.

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

أنا أؤيد الاحتفاظ بالخطأ! = لا تحقق كما هي.

في كل مرة أحفر فيها في قاعدة كود Go كبيرة ، أسأل نفسي كيف يمكنني تقليل بعض النماذج المعيارية. أعود دائمًا إلى:

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

يعد تعقب المشكلات مفيدًا للعديد من الأشياء ، ولكن الشيء الوحيد الذي لا يفيده هو إجراء مناقشة تفصيلية لموضوع معقد. لا يوفر متعقب المشكلة أي سلاسل رسائل ، والرد على رسالة معينة أمر محرج. نظرًا لعدم وجود اقتراح فعلي هنا ، مجرد استجابة لمقترحات أخرى ، أشجعك بشدة على أخذ هذه المناقشة إلى القائمة البريدية لـ golang-nuts.

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

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

إذا أخطأ! = لا شيء يشجع التعامل الجيد مع الأخطاء ، وهو التوثيق الذاتي ويشجع التوثيق الجيد للحالة المحددة ، وهو بصراحة أحد أكثر الأشياء التي أحبها في go. جعل تدفق التحكم الجديد هذا يقطع ، باستخدام عوائد ومعايير فوضوية وغامضة إلى حد ما ، والدلالات المربكة ليست في روح اللغة التي أعشقها.

الإسهاب ليس أمرا سيئا. الإسهاب غير الضروري ، لكنني أزعم أن معالجة أخطاء go ليست غير ضرورية. إنه جزء من سحر اللغة.

لا يمكن أن نتفق أكثر. يعد التعامل مع الخطأ الصريح أحد أفضل ميزات لغة IMO. أشعر دائمًا أن الكثيرين ممن يزعجهم ذلك لم يعتادوا عليه حتى الآن.

ليس من الجيد أن يتم فصل القضايا ، لكنني أعتقد أن رأيين مدمجين كرأي واحد في هذه الحالة.

  1. لا نحب بناء الجملة الجديد (جرب أو بناء جملة جديد إذا كان خطأ)
  2. على أي حال ، لا نريد إضافة بناء جملة جديد

لا يمكن لأيقونات التصويت GitHub تفسير الثانية.

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

لقد كتبت # 32811 وأنا أؤيد هذا الاقتراح أكثر ... أفضل ترك معالجة الأخطاء بمفردها. أعتقد أن ردود أفعال الرموز التعبيرية على هذا الاقتراح تقول الكثير.

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

بصفتي شخصًا أحدث في Golang ، فإن أحد الأشياء التي أجدها منعشة حول اللغة هي معالجة الأخطاء الصريحة. لقد عملت في Java و Ruby و Python و Node بكثافة ، والتعامل مع الأخطاء مرهق أكثر من Go. أفضل أن أرى "المسار" الواضح للأخطاء ، بدلاً من أن أتضح لي من خلال بعض التركيبات اللغوية التي تجعلها أكثر غموضًا.

ˋreturn ... إذا ... اقتراح andreynering هو في الواقع

موافق ، اترك if err != nil بمفرده.

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

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

من فضلك ، دعنا لا نضيف أشياء فقط من أجل إضافة الأشياء.

أنا أستمتع بساطة معالجة الأخطاء كما هي.

التوقع هو مجرد الجناس الناقص باستثناء ، وأنا أفضل عدم استخدامه. شكرا لبدء هذا.

من فضلك لا تغير الكأس المقدسة بلدي.

كان هناك الكثير من تعليقات المجتمع التي تطلب معالجة أكثر بساطة للأخطاء (من الاستطلاع السنوي). يعالج فريق Go الآن هذه المشكلة.

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

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

kevineaton هل تعتقد أن try معقد؟

وأنا أتفق مع هذا تماما. أنا لست حتى مقتنع شخصيا أننا بحاجة إلى أن تفعل أي شيء - وأنا أتفق if err != nil الشيكات تبدو محرجا للوهلة الأولى ولكن أنا لم أر أي شيء اقترح أن يحل في الواقع القضية دون انتهاك على نطاق واسع الأشياء ذاتها التي يذهب هو شعبية ل.

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

يعد تعقب المشكلات مفيدًا للعديد من الأشياء ، ولكن الشيء الوحيد الذي لا يفيده هو إجراء مناقشة تفصيلية لموضوع معقد.

أليست هذه الحقيقة. لكن ها نحن ذا.

if err != nil لن تختفي إذا تمت إضافة try . أعتقد أن try سيضيف بعض الوضوح إلى مسارات التعليمات البرمجية التي تكون إما إعادة توجيه أخطاء ثقيلة أو حيث يمكن تلخيص الكثير من الأخطاء المختلفة بسهولة في معالج خطأ واحد مؤجل. . لا أرى حقًا كيف أن try يشجع على عدم معالجة أخطاء أكثر من مجرد مجموعة فارغة من if-err-return-err . من السهل تجاهل معالجة الأخطاء فعليًا بغض النظر عن وجود try أم لا. أعتقد أن try هو أحد أفضل الاقتراحات لمعالجة الأخطاء حتى الآن لأنه يبدو أنه سيكون من السهل قراءة الكود الذي يستخدمه ..

سنتي غير المرغوب فيها ، لا يبدو الأمر وكأنه "اذهب". إنه أمر سحري للغاية ونحن نتجه نحو البنى الضمنية بدلاً من البنى الصريحة.

من الأسئلة الشائعة الخاصة بـ Go

لماذا لا تمتلك Go عامل التشغيل؟:؟
_لا توجد عملية اختبار ثلاثية في Go. يمكنك استخدام ما يلي لتحقيق نفس النتيجة: _

if expr {
   n = trueVal
} else {
    n = falseVal
}

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

تضمين التغريدة

يعد تعقب المشكلات مفيدًا للعديد من الأشياء ، ولكن الشيء الوحيد الذي لا يفيده هو إجراء مناقشة تفصيلية لموضوع معقد. لا يوفر متعقب المشكلة أي سلاسل رسائل ، والرد على رسالة معينة أمر محرج. نظرًا لعدم وجود اقتراح فعلي هنا ، مجرد استجابة لمقترحات أخرى ، أشجعك بشدة على أخذ هذه المناقشة إلى القائمة البريدية لـ golang-nuts.

يمكنك الرد على رسالة معينة. أنا فقط رددت لك. :)

نظرًا لعدم وجود اقتراح فعلي هنا ، مجرد رد على مقترحات أخرى ،

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

بعد قضاء عشر سنوات في كتابة Java و Python ... أقوم أيضًا بتدريس علوم الكمبيوتر بالإضافة إلى عملي اليومي كمهندس برمجيات

kevineaton هل انتهيت من مص

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

عدم تغيير اللغة لإزالة if err != nil هو اقتراح شديد الأهمية لا يحتاج أساسًا إلى تفاصيل إضافية. لست متأكدا ما هي المشكلة. لا ، ليس الله فظيعًا طويلًا ويصعب التذمر. هذا لا يجعلها خاطئة أو سيئة أو غير كافية.

+1 ، إذا لم يكن هناك شيء أفضل ، فالشيء الجيد سيكون معلومات تتبع تكدس جيدة حقًا (بدون إطارات لأشياء الرقص) ، أعتقد أن x/errors حقق هذا بالفعل ، لكني أحب شيئًا سريعًا في المستقبل القريب ، مثل وضع العلامات func s باستخدام الكلمة الرئيسية throws التي من شأنها إرجاع كلمة رئيسية error + try ، مما يمنع تظليل الخطأ المتغير (الذي أكرهه شخصيًا) ، شيء من هذا القبيل:

func a() (int) throws {
  throw &someError{}
}

anInt, err := try a()

icholy كان هذا لا مبرر له بشكل لا يصدق. هذا مكان للمناقشة ومن المفترض أن يكون مجتمع Go مجتمعًا ترحيبيًا. لا يوجد مكان لهذا النوع من الملاحظات. أعتقد أن سقراط كان لديه ما يقوله عن الإهانات في المناظرة.

معالجة الخطأ الحالية عرضة للخطأ البشري. من السهل أن تنسى التحقق من err في الوقت الحالي. إذا كان هناك أي عمليات تحقق بالفعل في النطاق (وفي معظم الأوقات) ، فلن ينتهي المترجم بـ unused variable . يجب أن يكون التعامل مع الخطأ صارمًا - إما أن تكون _ خطأ أو تتحقق منه - لا ينبغي أن يكون إطلاق النار على الساق ممكنًا.

kevineaton هل تعتقد أن try معقد؟

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

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

كتل try تهزم الغرض الكامل من عمليات الإرجاع المتعددة.

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

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

fillest التغيير المقترح في معالجة الأخطاء يجعل "

توقفت عن استخدام Go بسبب نقص الأدوية الجنيسة ، والنزعة المعيارية ، و GC ، ونقص حدود الموارد / المحاسبة وعبء العمل الناتج عن PHP noobs الذي لم يفهم ما يفعله المترجم. حل Haskell و C # وآخرون معالجة الأخطاء بشكل جيد ... يبدو اقتراح Go 2 جيدًا إذا كان لديه معالجة واضحة للحالة كما كان من قبل (غير متأكد).

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

موافق تمامًا ، نظرًا لأن العنصر المدمج try لن يساعد في التفاف الأخطاء وإضافة المعلومات إليها حتى ولو قليلاً.

قبل إعادة الكتابة بـ try :

_, err := doSomething()
if err != nil {
    return nil, errors.Wrap(err, "failed to do something")
}

_, err = doOtherThing()
if err != nil {
  return nil, errors.Wrap("failed to do the other thing")
}

تخيل ماذا سيحدث بعد إعادة الكتابة بـ try .

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

try(extract_value(try(get_data(1), errors.Wrap(err, "failed to get data")), errors.Wrap(err, "failed to get data")))

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

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

if err != nil {
    return _, _, err
}

sorenvonsarvort لا يبدو هذا سيئًا بالنسبة لي:

var errContext string 

defer func() {
  // err is a named return
  if err != nil {
    err = fmt.Errorf("%v: %w", errContext, err)
  }
}()

errContext = "failed to do something"
_ := try(doSomething())

errContext = "failed to do other thing"
_ := try(doOtherThing())

حسب فهمي ، لا يزال بإمكانك أيضًا استخدام if err != nil { ... } إذا كان أكثر وضوحًا لهذا القسم المحدد من التعليمات البرمجية.

يتألق try في حالات أخرى. تخيل شيئًا مثل:

func trySomeComplexOp() (r result, err error) {
  a := try(step1())
  b := try(step2(a))
  c, d := try(step3(b))
  return try(lastStep(c, d)), nil
}

يمكن أن يكون الكود مثل الكود أعلاه أنظف كثيرًا مما لو كان عليك رش كتل if err != nil . تدور فكرة Go حول "سهولة القراءة الخطية" لذا أعتقد أن try يعمل جيدًا لتحقيق هذه الغاية.

كان هناك الكثير من تعليقات المجتمع التي تطلب معالجة أكثر بساطة للأخطاء (من الاستطلاع السنوي). يعالج فريق Go الآن هذه المشكلة.

هذه أقلية صوتية وأراهن أن جزءًا كبيرًا منهم لا يستخدم Go

sirkon على ماذا

sorenvonsarvort لا يبدو هذا سيئًا بالنسبة لي:

يمكن أن يكون الكود مثل الكود أعلاه أنظف كثيرًا مما لو كان عليك رش كتل if err != nil . تدور فكرة Go حول "قابلية القراءة الخطية" لذا أعتقد أن try يعمل جيدًا لتحقيق هذه الغاية.

في روسيا نسمي هذا «экономия на спичках». استخدم مترجم جوجل للحصول على معنى.

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

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

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

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

يصبح

f := try(os.Open(filename), "open data file")

بالطبع ، إذا كنت بحاجة إلى القيام بأكثر من ذلك بكثير ، فإن الطريقة "الكاملة" للقيام بشيك err != nil لا تزال متاحة.

أوافق على هذا ، لكنني سأحترم الطلب المقدم من فريق go للحصول على مزيد من الخبرة في التغيير قبل الحصول على رأي نهائي.

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

أعترف بأن الأشخاص الآخرين قد يعاملون الأخطاء بشكل مختلف عني وأعترف بإمكانية استخدام try بطريقة إيجابية. يحتوي الكود المصدري tryhard نفسه على بعض الحالات المتتالية من return err ، إذا كان سيتم استخدام try فلا أعتقد أنه سيتم اختراق الوضوح بدرجة كبيرة. لكنني أخشى فقط من سوء الاستخدام ، لأن ذلك سيؤثر على الوضوح. يتم توفير مثال جيد هنا . ثم تحديد ما هو الاستخدام الجيد أم لا سيكون قصة أخرى كاملة.

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

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

كان هناك الكثير من تعليقات المجتمع التي تطلب معالجة أكثر بساطة للأخطاء (من الاستطلاع السنوي). يعالج فريق Go الآن هذه المشكلة.

icholy كما قلت ، أود إضافة سياق إلى الأخطاء. في هذا الصدد ، يعني "التعامل مع الأخطاء بشكل أكثر بساطة" بالنسبة لي طرقًا أفضل لتوفير هذا السياق والحصول على المعلومات منه. على سبيل المثال ، مع كل السياق الذي أضفته إلى أخطائي ، يجب أن يكون تافهًا لسؤال "السياق" إذا كان الخطأ ناتجًا عن انتهاء المهلة. لكنها ليست كذلك. عادة ما تضطر إلى اللجوء إلى استخدام pkg/error ، وإنشاء "هياكل خطأ" خاصة بك و / أو إنشاء طرق للعمل معها والتي يمكن أن تلجأ إلى إجراء عمليات بحث متسلسلة بناءً على التنفيذ. أفضل أن أرى شيئًا من شأنه أن ينقذني من إنشاء هياكل وطرق كاملة بدلاً من الأشياء التي توفر لي if واحدًا من حين لآخر في أحسن الأحوال. وكما قيل سابقًا ، هل يمكنك حقًا تسميتها "معالجة الخطأ" عندما يوفر هذا التغيير في الأساس طريقة أكثر ملاءمة لعدم معالجة الخطأ حقًا؟

تعتقد أن try معقد؟

في العزلة ، فإن try ليس معقدًا ، ولكن لا يتم اعتبار تغييرات اللغة بمعزل عن غيرها. انصح:

  • زيادة الحمل المعرفي من تعلم دلالات بناء جديد
  • انخفاض في سهولة القراءة بسبب استخدام try لأنه أقصر ، في الحالات التي يجب استخدام if err != nil {return ... errors.Wrap() } بدلاً من ذلك

أعبر عن المشاعر التي تقول إن البساطة (وجود طريقة واحدة للتحقق من الأخطاء) أكثر أهمية من طريقة مختصرة للتحقق من الأخطاء.

أليست طريقة التعامل مع الخطأ هي من خلال ذلك البيان العالمي وإذا كان هذا أمرًا يجب تجاهله ، فكيف يتم التعامل مع حالات الذعر ؟؟ أود أن أؤيد معالجة أفضل للخطأ لأن نماذج البرمجة الأخرى تتعامل مع الأخطاء اليوم ولكن لا يتم تجاهلها

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

ماذا لو تم تغيير gofmt بحيث يكون البيان الواحد إذا بقيت الكتل على سطر واحد؟

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

إليك ما سيبدو عليه الأمر:

// we already have an err in scope
err = frub.Confozzle(foo, bar, baz)
if err != nil { return errors.Wrap(err, "confozzling didn't work") }

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

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

أنا أتفق تماما مع تركها كما هي. إنها مطولة بعض الشيء ولكن من السهل جدًا اتباعها.

إذا كان بإمكاني التقليل

if err := foo.x(a, b); err != nil {
    return err
}

if err := foo.y(); err != nil {
    return err
}

if err := foo.z(c); err != nil {
    return err
}

إلى شيء مثل

if err := check foo.x(a, b), foo.y(), foo.z(c); err != nil {
    return err
}

ربما سيكون أيضًا رائعًا دون تغيير المصطلح كثيرًا IMHO.

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

sanbornm ، أنت على حق. أنا موافق.

يمكن أن يكون هذا check أيضًا عامل تشغيل checkNonZero لن يأخذ سوى وسيطة إرجاع واحدة ويعود عند القيمة الأولى غير الصفرية (مثل null). ولكن إلى جانب كونها غامضة للغاية ، وتؤثر بشكل أكبر على اللغة. حتى أنه يعطي تلميحًا طفيفًا أنه لا يجب عليك استخدامه إذا كنت تتوقع ، على سبيل المثال ، io.EOF. الاحتمال الآخر هو ، ربما ، الاعتماد على go vet على الأقل لإعلامك بالحالات الأكثر شيوعًا (مثل io.EOF عند القراءة من أنبوب) ... لكن هذه الفكرة لا تبدو جيدة في كل شيء بالنسبة لي.

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

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

في المشاريع الكبيرة ، يتم إنشاء أسوأ السجلات من خلال وظيفة نسينا أن شخصًا ما كتبها ، والتي تستدعي مكتبة تستدعي مكتبة تستدعي مكتبة تطرح رقمًا عامًا new Exception() لم يتم اكتشاف أي شيء حتى فعل معالج استثناء "Pokemon" وتسجيل خطأ عام. ثاني أسوأ شيء هو نفسه ، ولكن مع تتبع مكدس غامض من عدة مئات من الأسطر ، والذي أعتقد أنه يمكننا في النهاية معرفة السبب (لحسن الحظ ، البحث عن github.com/<us>/<ourproject> يجد معظم المعلومات ذات الصلة ، ولكن هناك الكثير في بعض الأحيان). على الرغم من اسمها ، فإن "الاستثناءات" غير استثنائية بشكل مؤلم في مشاريع Java الكبيرة.

في هذه الأثناء ، حتى عندما يكون هناك الكثير من السياق الزائد ، فإن سلاسل خطأ Go البسيطة مثل "narf: Error unpoiting the zort: foo: Unexpected bar in baz: {\"ork\": \"morpork\"}" كانت (من واقع خبرتي) سهلة التفسير عادةً ، طالما كنا حريصين على تضمين السياق المهم في مكان ما في قيمة الخطأ الفعلية. إذا تبين أن السياق المهم مفقود ، فعادةً ما يكون ذلك واضحًا إلى حد ما. يضيف "الإصلاح" في هذه الحالات مزيدًا من السياق وينتظر خطأ آخر ، لذلك فهو ليس مثاليًا ، ولكن بشكل عام ، ما زلت أفضل هذا على التمرير عبر تتبعات المكدس و / أو الاعتماد على تبعيات تبعيات تبعياتي "لرمي أو "رفع" رسائل خطأ عقلانية. إنني أقدر حقًا كيف يبدو أن الاسم panic() يمنع معظم مطوري Go من النشر الحر لما هو أساسًا لميزة اللغة نفسها. دعونا لا نجعل error و panic نفس الشيء بعد كل شيء.

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

func foo(a, b, c SomeArgType) (x, y, z SomeReturnType, err error) {
  handleError := func(handleErr error) (x, y, z SomeReturnType, err error) {
    log.WithFields(logrus.Fields{
      "package": "foo",
      "func": "foo",
      "arguments": map[string]SomeArgType{"a": a, "b": b, "c": c},
      "error": handleErr,
    }).Error("Error fooing the bar")
    return reasonable, default, values, handleErr
  }

  err := doABunchOfThings()
  if err != nil {
    return handleError(err)
  }
}

وهو ، باعتراف الجميع ، لا يزال حلاً ناقصًا من بعض النواحي. لكني أحب أن القيام بذلك جعل من السهل جدًا على المطورين المستقبليين أن يستمروا في فهم متى وماذا يعود foo ، دون التحكم في تدفق التدفق كثيرًا.

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

تضمين التغريدة

من شأن التفاف الخطأ وتكديس الإطار / طباعة الخطأ أن يجعل الكثير من الأمور المباشرة

أنا موافق.

أنا شخصياً أفضل الانتظار لتقديم ميزات go2 أكثر قوة حتى تتم تسوية تعدد الأشكال البارامترية لأنه قد يكون مفيدًا عند تصميم باقي الميزات

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

تضمين التغريدة

كان هناك الكثير من تعليقات المجتمع التي تطلب معالجة أكثر بساطة للأخطاء (من الاستطلاع السنوي). يعالج فريق Go الآن هذه المشكلة.

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

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

لا أؤيد بشدة التعامل مع الخطأ ، ولكن يمكن أن يكون حجة لترك الأشياء كما هي.

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

تضمين التغريدة

كان هناك الكثير من تعليقات المجتمع التي تطلب معالجة أكثر بساطة للأخطاء (من الاستطلاع السنوي). يعالج فريق Go الآن هذه المشكلة.

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

لا تحتاج إلى النظر في مطالبة هذا الشخص بجدية: يُظهر عدد Emojis أن الأشخاص عمومًا لا يحبون اقتراح try وهم عمومًا يحبون هذا العرض "اتركه كما هو".

PS في ممارستي ، لم يستخدمها الغالبية العظمى من الأشخاص الذين لا يحبون Go في مجالها الرئيسي (خدمات الشبكة ، أدوات CLI). لذلك أفضل تجاهل آرائهم.

نحتاج إلى خيارات أفضل وأقل إثارة للجدل من عرض try .
لا أرى ضرورة ملحة لإيجاد حلول متسرعة.

velovix أعتقد أنني أكره تعدد الأشكال البارامترية أكثر من محاولة / التقاط الخطأ "معالجة" ، ولكن إذا أصبحت ميزة لغوية ، يمكنني رؤية بعض الطرق لتتجنب الحاجة إلى ميزة لغة مضمنة أخرى.

لسبب واحد ، عندما يكون الكود الذي لا يرغب الناس في تكراره يكون نموذجيًا مثل:

foo, err := Foo()
if err != nil {
  log(err)
}
bar, err := Bar(foo)
if err != nil {
  log(err)
}
// ...

ثم بعض الدوال البارامترية ، واستدلال النوع ، وأنماط تصميم كائن النمط _maybe_ أو _ الاختياري __ الاختيارية ستقلل بشكل مباشر (heh) من النموذج المعياري دون اللجوء إلى استراتيجيات تدفق تحكم غير خطية غريبة:

func<T> DoWithErrorLogging(f func(any...) (T, error), args... any) T {
  t, err := f(args...)
  if err != nil {
    log(err)
  }
  return t
}
// ...
foo := DoWithErrorLogging(Foo)
bar := DoWithErrorLogging(Bar, foo)

IMO كل هذا سيكون أسوأ بكثير من Go1. ولكن أفضل من امتلاك هذا _plus_ حاول / التقط الكلمات الرئيسية في اللغة.

بصراحة ... في الوقت الحالي ، أعتقد أن التغييرات "المعطلة" المفضلة لدي لـ Go2 ستعمل فقط على إصلاح جميع المضايقات الصغيرة في Go1 ، مثل القيم الافتراضية net/http التي تكون متداخلة داخل globals المشتركة القابلة للتغيير (فقط اجعل Hashicorp's cleanhttp قياسيًا ، بشكل أساسي) ، أو (*time.Timer).Reset() لها قيمة إرجاع عديمة الفائدة عليك فقط معرفتها ، أو حزمة syscall بأكملها . يمكن إطلاق Go3 على الفور تقريبًا بعد ذلك مع أي أورام يرغب الناس في النمو عليها ؛ لا أفهم سبب الحاجة إلى إجراء التغييرات الصغيرة والكبيرة في إصدار واحد.

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

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

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

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

إذن هذا أفضل:
القيمة ، يخطئ: = foo ()
العودة يخطئ إذا يخطئ! = لا شيء

لكن لا يزال هذا شائعًا جدًا. لذا أود أن يكون smt مثل هذا ممكنًا بطريقة ما:

القيمة ، تحقق من الخطأ: = foo ()

إذا أراد Go أن يكون لغة قابلة للقراءة ، فيجب تقليل القدرة على القيام بالأفكار التي يصعب قراءتها أو فهمها.

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

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

icholy يرجى أن تكون مهذبا. يرجى العلم بمدونة قواعد سلوك Gopher: https://golang.org/conduct. شكرا.

الجميع: بالإضافة إلى تعليقي أعلاه (https://github.com/golang/go/issues/32825#issuecomment-506740412) يرجى العلم بـ https://golang.org/wiki/NoPlusOne. ليس من المفيد الإدلاء بتعليق يقول أكثر بقليل من "أوافق" أو "لا أوافق". استخدم أزرار الرموز التعبيرية بدلاً من ذلك. شكرا.

تضمين التغريدة

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

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

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

(أفهم أن الاقتراح B في هذه الحالة مغلق ؛ حقيقة أن هذا الاقتراح يحتوي على 77 تعليقًا في أقل من يوم يوضح السبب. هذا المستوى من المناقشة يعمل ببساطة بشكل أفضل على قائمة بريدية بدلاً من أداة تعقب المشكلات.)

تضمين التغريدة

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

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

نظرًا لأن هذه المشكلة مقفلة ، لم يعد من الممكن "التصويت" عليها باستخدام Emojis. لهذا السبب أعتقد أن هذه المشكلة تم إنشاؤها في المقام الأول.

نقطة جانبية ولكنها مرتبطة ، لم يتم التعامل مع إدارة التبعية بشكل جيد. عمل ديب بشكل رائع وتم اختياره (شكل ما بدا) من العدم [1]. أفهم أن هذا هو سبب إنشاء نظام الاقتراح. أشعر فقط أن نظام الاقتراح في هذه الحالة قد يقلل تمثيل المجتمع إذا تم قفل المشكلات ويطلب منا الانتقال إلى القوائم البريدية.

[1] https://twitter.com/_rsc/status/1022588240501661696

تحرير: يقوم فريق Go والمجتمع في معظم الأحيان بعمل رائع بالاستماع إلى تعليقات المجتمع. أنا أقدر كل العمل الذي يدخل فيه. تعد استطلاعات Go مثالًا رائعًا.

تضمين التغريدة

عملت ديب بشكل رائع

بحاجة إلى الاختلاف هنا. حلت وحدات Go أخيرًا مشكلة "gobindata" غير المعروفة من خلال التخزين المؤقت المستمر عن طريق https://proxy.golang.org

لم يدرك هذا الرجل المتأنق المشكلة وكان يتلاعب بـ "ضمان" خيالي عبر VCSes بدلاً من ذلك.

sirkon هذا

كما هو الحال ، أعتقد أنني أفضل ترك الأشياء كما هي ما لم تتم إضافة المزيد من القيود ، مثل محاولة عبارة واحدة في كل سطر. السبب هو النظر في هذا المثال من الاقتراح - يبدو أنه غير ضار بما يكفي info := try(try(os.Open(file)).Stat()) لكنه يتسرب من مقابض الملفات خارج نطاق تدفق التحكم العادي. أعتقد أننا سنشهد زيادة في تسريبات موارد الملفات باستخدام تطبيقات io.Closer أو وظيفة تنظيف أخرى قد يتجنبها الأشخاص في السعي وراء كود أكثر إحكاما.

ربما سيعتبر بعض الأشخاص أنه غير مهم لأن f لن تكون حية ، وبالتالي فهي مؤهلة للحصول على GC على الفور وفي وقت ما سيضمن المصير النهائي أن f مغلق. أعتقد أنه يغير الاتفاقيات السابقة الواضحة (المدعومة من linter) لاستخدام الإرجاء اليوم المرتبط بكتلة الوظيفة. عندما يتم الخروج من كتلة الوظيفة ، يتم تحرير المورد. لا يوفر الاعتماد على أداة تجميع البيانات المهملة أي ضمانات بعدم استنفاد الموارد (يمكن أن تتراوح الإعدادات الافتراضية لحد التعامل مع الملفات المفتوحة النموذجية بين 1 كيلو و 4 كيلو) - والتي يمكن تجاوزها بسهولة بمسار ملف بسيط.

باختصار ، أعتقد أن بناء الجملة هذا كما تم تنفيذه يوفر مخاطرة دقيقة في إدارة الموارد في Go لأنه يفتقر إلى ctor / dtor ويعتمد على آليات GC ذات المستوى الأدنى بعيدًا عن كتل التعليمات البرمجية لمنع تسرب الموارد. تحويل شيء يبدو غير ضار إلى حالة خطأ محتملة (عدد كبير جدًا من الملفات المفتوحة).

var n int
for _, name in try(os.Readdir(...)) {
   n += try(getSize(name))
}
func getSize(name string) (int, error) {
   return try(try(os.Open(name)).Stat()).Size
}

تعديل:
بالنسبة للقيود ، أعتقد أنه إذا كان صحيحًا فقط على الجانب الأيمن من المهمة ، فسيكون ذلك أفضل من قول 1 لكل سطر ، نظرًا لأن a, b := try(get("a")), try(get("b")) معقول بما فيه الكفاية. لكنه لا يزال يترك القدرة على أداء try(os.Open(name)).Stat() - وهو ما إذا كنت ستجعل المحاولة () باطلة ، ولكن فقط عندما لا تكون في RHS لمهمة ما ، فأنت تترك شيئًا لا يعمل كثيرًا كما هو الحال في الكل.

cstockton اكتشاف رائع رائع!

يحتوي Rust في الواقع على ماكرو مشابه ( ? إذا كنت أتذكر بشكل صحيح) والذي يقوم بالضبط بما يعنيه try ، لكن لديهم لغة raii مناسبة هناك ، لذا فهي ليست مشكلة في تلك اللغة بل إنها ضخمة ثقب في حالتنا

sanbornm نعم ، الاحتفاظ بنصف الإنترنت في الريبو الخاص بك يبدو فكرة رائعة حقًا.

Finishing Touch

نظرًا لأن شخصًا ما ذكر أداة مساعدة تحسب عدد الأماكن التي سيوفر لي فيها try الوقت / الجهد ، فقد قررت تشغيلها على أكبر مشاريع العمل الخاصة بي ، بالإضافة إلى كل شيء آخر في دليل GOPATH GitHub القديم.

| مشروع | LOC * | جرب المرشحين |
| ---------- | ------ | ---------------- |
| كال 1 | 2047 | 3 |
| مضخة 1 | 1030 | 0 |
| مستندات 1 | 4576 | 8 |
| hugoutil | 604 | 1 |
| كل شيء آخر | 8452 | 23 |

  • اذهب إلى الكود فقط ، باستثناء التعليقات ، وفقًا للأداة المساعدة cloc .

ضع في اعتبارك أن محتويات "كل شيء آخر" تتضمن اختراقات سريعة ورمزًا كتبته عندما كنت أتعلم Go.

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

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

miekg الرجاء إضافة هذا الرابط https://github.com/golang/go/issues/32825#issuecomment -506882164 في الاقتراح. المثال يستبعد تمامًا الفكرة الكاملة لهذه الكلمة الرئيسية الأخيرة try .

image

أنا أتفق تماما مع تركها كما هي. إنها مطولة بعض الشيء ولكن من السهل جدًا اتباعها.

إذا كان بإمكاني التقليل

if err := foo.x(a, b); err != nil {
  return err
}

if err := foo.y(); err != nil {
  return err
}

if err := foo.z(c); err != nil {
  return err
}

إلى شيء مثل

if err := check foo.x(a, b), foo.y(), foo.z(c); err != nil {
  return err
}

ربما سيكون أيضًا رائعًا دون تغيير المصطلح كثيرًا IMHO.

إذا كنت تتحدث عن نوع "ربما" ، فإنه يتطلب نوعًا متغيرًا أولاً.

دعنا نحافظ على if err != nil أنه يعمل ، إنه واضح ، إنه ليس مطولًا حقًا ، وهو أمر منطقي في تدفق الكود. عندما تقرأ الكود باستخدام هذا البناء ، فأنت تعرف ما الذي ستفعله.
دعونا نحتفظ بها ، دعونا لا نضيف try

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

الثلاثة أحرف "يخطئ" في نفس المستوى مقبولة بالنسبة لي. لا أرغب في بعض وظائف "التحقق" التي تغلف الكود المهم ، لأن الكود المهم سيكون في المستوى الثاني (تذكر lisp؟) ، ولا أرغب في "try" سطرًا من قبل ، لأن الرمز المهم سيظهر مسافة بادئة وفي السطر الثاني.

الدقة ، يخطئ: = start_job ()
إذا أخطأت! = لا شيء {
مقبض_خطأ ()
}

يخطئ = Continue_job (الدقة)
إذا أخطأت! = لا شيء {
مقبض_خطأ ()
}

باستخدام هذا الرمز ، يمكنك قراءة تدفق حالة عدم الخطأ من خلال قراءة الأسطر الأولى من الكتل (حيث أقرأ عناوين المستندات عندما يتعين علي قراءتها بسرعة)

نظرًا لأن شخصًا ما ذكر أداة مساعدة تحسب عدد الأماكن التي سيوفر لي فيها try الوقت / الجهد ، فقد قررت تشغيلها على أكبر مشاريع العمل الخاصة بي ، بالإضافة إلى كل شيء آخر في دليل GOPATH GitHub القديم.

مشروع LOC * جرب المرشحين
كال 1 2047 3
مضخة 1 1030 0
docs1 4576 8
hugoutil 604 1
كل شيء آخر 845223

  • اذهب إلى الكود فقط ، باستثناء التعليقات ، وفقًا للأداة المساعدة cloc .

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

أعتقد أن هناك حاجة إلى المحاولة أكثر في البرامج الأكبر.

نقطة جيدة. حاولت tryhard على heptio / contour ، 28.7 ألف سطر من النص المصدر ، وجد tryhard 12 استبدالًا.

أعتقد أن هناك حاجة إلى المحاولة أكثر في البرامج الأكبر.

نقطة جيدة. حاولت tryhard على heptio / contour ، 28.7 ألف سطر من النص المصدر ، وجد tryhard 12 استبدالًا.

رائع! 12 مقابل 28.7 ألف سطر ، هذا يحتاج حقًا إلى كلمة رئيسية مخصصة!

حسنًا ، أنا مهتم أكثر بنظرك الشخصي بشأن هذا :

stat := try(try(os.Open(fileName)).Stat())

أعتقد أنه أكثر شيوعًا إذا كان برنامجك أكثر تماسكًا وليس جزءًا من تكامل الخدمة بين العديد من الخدمات. عندما أبحث فقط عن fmt.errorf أو errors في github على هذا المستودع ( heptio/contour ) ، لا توجد سوى نتائج قليلة جدًا ، لذا من الصعب الحصول على نظرة عامة سريعة .. ولكن كما أنا قال إنه ربما يختلف كثيرًا من برنامج إلى آخر ، حتى بالنسبة للبرامج الأكبر.

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

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

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

ليس لديك فكرة. التعليقات التوضيحية هي للعثور بسهولة على طريقة حدوث خطأ. لدينا بالمثل os.NotExist لكن هذا بالكاد تلميح جيد لمسار الخطأ.

thomasf هنا نقطة بيانات أخرى ، من نسخة عمل عمرها عدة سنوات من juju / juju ،

529628 مصدر خطوط ، وجد tryhard 1763 (0.3٪) بدائل.

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

529628 مصدر خطوط ، وجد tryhard 1763 (0.3٪) بدائل.

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

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

لن أتخذ موقفا بشأن ذلك. ومع ذلك ، فإن ما أجده هو القليل من الأدلة الداعمة التي تشير إلى ذلك

أ. هناك الكثير من المواقع التي ينطبق فيها try على قواعد كود go الموجودة
ب. تشكل معالجة الأخطاء بشكل عام جزءًا كبيرًا من SLOC ، بناءً على قياساتي الخاصة والأرقام التي طرحها فريق Go ، راجع https://youtu.be/RIvL2ONhFBI؟t=440 timecode 07:26

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

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

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

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

var v1, v3 string
if err := func() error {
    try(onething())
    v = try(twothing())
    try(otherthing())
    v3 = try(somethingg())
}(); err != nil {
  ... handle error...
}

أعتقد أنه قد يكون من الجيد في هذه المرحلة كتابة موقع ويب للاحتفاظ ببيانات tryhard على حزم مختلفة وتصورها. ربما يمكنك تعديل القليل من golang / gddo (godoc.org) للقيام بهذه المهمة.

أفضل ترك if err != nil بمفرده. ولكن إذا كان علينا إضافة شيء ما لمعالجة الأخطاء ، فإليك اقتراحًا جديدًا يضيف كلمة رئيسية throws له.

32852

بدون تكرار بعض الحجج الموضحة بالفعل هنا ، أردد المشاعر لترك if err != nil كما هو.

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

هناك مخاوف أكبر يمكننا معالجتها في اللغة ورد الفعل الواضح للمجتمع على هذه المشكلة يقول أن if err != nil ليس واحدًا منهم.

Go مثالي لأسباب عديدة. أهمها "إذا أخطأ! = لا شيء". قد يبدو الأمر مطولًا ، ولكن بالنسبة للأشخاص الذين يتعلمون البرمجة ، فإنه يجعل من السهل تصحيح التعليمات البرمجية وتصحيح الكود.

تضمين التغريدة

لن أتخذ موقفا بشأن ذلك. ومع ذلك ، فإن ما أجده هو القليل من الأدلة الداعمة التي تشير إلى ذلك

أ. هناك الكثير من المواقع التي ينطبق فيها try على قواعد كود go الموجودة
ب. تشكل معالجة الأخطاء بشكل عام جزءًا كبيرًا من SLOC ، بناءً على قياساتي الخاصة والأرقام التي طرحها فريق Go ، راجع https://youtu.be/RIvL2ONhFBI؟t=440 timecode 07:26

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

هذا مثال:

llorllale:~/go/src/github.com/hyperledger/fabric$ cloc --exclude-dir=vendor .
    2406 text files.
    2256 unique files.                                          
    3130 files ignored.

http://cloc.sourceforge.net v 1.60  T=6.69 s (272.8 files/s, 58350.9 lines/s)
--------------------------------------------------------------------------------
Language                      files          blank        comment           code
--------------------------------------------------------------------------------
Go                             1751          54365          34149         294005
YAML                             35            547           2171           2060
Bourne Shell                     26            354            325           1312
make                              3            135             96            418
CSS                               1             40             14            140
HTML                              3              7              5             63
Python                            1             50            103             57
Bourne Again Shell                1              1              6             50
Java                              3              7              4             26
XML                               2              1              4              2
--------------------------------------------------------------------------------
SUM:                           1826          55507          36877         298133
--------------------------------------------------------------------------------
llorllale:~/go/src/github.com/hyperledger/fabric$ tryhard -l . | grep -v vendor | less | wc -l
1417

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

if err != nil {
   return errors.Wrap(err) 
} 

...
if err != nil {
   return errgo.Notef(err, "error doing x") 
} 

لن يتم الإبلاغ عنها من قبل tryhard.

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

Language                             files          blank        comment           code
---------------------------------------------------------------------------------------
Go                                    2488          40317          15901         297038

tryhard تقارير 2736 البدلاء، لكنه لا يفعل مراجعة اليدوي تبدو التفاف من هذا القبيل undercounts المتبقية بنحو عام 1850، لذلك أود أن تقدير ما مجموعه 4500 ~ try الأعراف في منطقتنا مصدر برنامج من 300K خطوط.

(أنا شخصياً أؤيد الصراحة الحالية لمعالجة الأخطاء ولا تمانع في ذلك).

على سبيل المثال ، إذا كانت اتفاقية شركتك تريد
[التفاف الأخطاء برسالة مخصصة]
لن يتم الإبلاغ عنها من قبل tryhard.

هذه هي النقطة - اقتراح try يبسط فقط if err != nil return err العوائد المجردة ، ولا يدعم أخطاء الالتفاف مع رسالة وسياق مخصصين.

التكرار الوحيد لـ if err != nil أعتقد أنه يمكن إصلاحه من خلال تحديد القيم الصفرية لقيم الإرجاع الأخرى. يمكن تحديث اللغة لإلغاء ذلك. على سبيل المثال:

في Go Today ، إذا كانت لدي وظيفة بهذا التوقيع:

func add(x, y string) (int, error)

في مكان ما في الوظيفة ، يجب أن أكتب:

func add(x, y string) (int, error) {
    // ...
    if err != nil {
        return 0, err
    }

إجبار الكاتب على تكرار نفس القيم الصفرية في جميع أنحاء الوظيفة.

سيكون من الأسهل كثيرًا (وبتكلفة قليلة لخطأ الإسهاب وقابلية القراءة) إذا تمكنت اللغة تلقائيًا من ملء القيم الصفرية لقيم الإرجاع غير الخطأ:

func add(x, y string) (int, error) {
    // ...
    if err != nil {
        return ..., err
    }
    // ...
}
func main() {
    add("8", "beep") // returns 0, error(`strconv.ParseInt: parsing "beep": invalid syntax`)
}

يمكنني القول من تجربة مع الكثير من التعليمات البرمجية التي تتفاعل مع استعلامات ومكالمات قاعدة البيانات ، فإن الحاجة إلى تكرار القيم الصفرية عبر الوظائف هي السلبية الوحيدة لمعالجة أخطاء نمط Go. وإلا فأنا أتفق مع وجهة نظر هذا الاقتراح: اترك if err != nil بمفرده!

ملاحظة: نعم ، يمكن لقيم الإرجاع المسماة _sort of_ تحقيق هذا (https://play.golang.org/p/MLV8Y52HUBY) ، ولكن بعد تنفيذ بعض الوظائف في قواعد الأكواد الخاصة بي باستخدام هذه التقنية ، تم تذكير كم من القدم - قيم الإرجاع المسماة بالبندقية هي ؛ دائمًا ما ينتهي بي الأمر بتظليل قيمة الإرجاع المسماة.

على سبيل المثال ، إذا كانت اتفاقية شركتك تريد
[التفاف الأخطاء برسالة مخصصة]
لن يتم الإبلاغ عنها من قبل tryhard.

هذه هي النقطة - اقتراح try يبسط فقط if err != nil return err العوائد المجردة ، ولا يدعم أخطاء الالتفاف مع رسالة وسياق مخصصين.

هذا صحيح ، كنت أفكر في المتغير الذي يسمح بإضافة سلسلة وصفية. الغالبية العظمى (4000/4500 تقريبًا) من مرتجعات الأخطاء هي بلا سياق errgo.Mask(err) ، والذي كنت أفكر فيه أنه يعادل عدم الوصف try() ، لكنه حاليًا سيكون انخفاضًا في وظيفة منذ يرجو إضافة معلومات المكدس والمحاولة لا (حتى الآن).

ianlancetaylor هناك اقتراح هنا. تقترح miekg أنك بصفتك أحد قادة لغتنا لم تعد تسعى لاستبدال if err != nil ببعض التركيبات الأخرى التي تتعارض مع روح التعامل مع الخطأ كما قرر مؤلفو Go الأصليون. بالنسبة لي شخصيًا ، يبدو أنك تحاول التأكيد على عدم أهمية هذا السؤال عن طريق نقله إلى golang-nuts بدلاً من التعامل معه مثل مقترحاتنا الأخرى. قد لا يكون هذا هو نيتك ، لكنه التأثير الذي أشعر به.

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

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

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

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

تمت مناقشة الموضوع بالفعل في مجموعة Google أيضًا.

النسخة الحالية من # 32437 غير مرضية. تخفي المحاولة () المدمجة الكثير من مسارات التنفيذ للعين غير المدربة. كان الاقتراح الأصلي مع التحقق والمقبض مفهومًا للغاية وكانت كلمة التحقق الرئيسية بارزة.

الآن ، تبدو try () builtin كدالة - ليس من الواضح أنها يمكن أن تغير التحكم في التدفق. لدينا أيضًا حالة من الذعر () ، لكنها (على ما أعتقد) دائمًا على خط خاص بها ، ولها اسم بارز ونادر استخدامها. try () من ناحية أخرى ، يمكن أن تختبئ داخل تعبير معقد.

صممtheckman Robert التكرارات الأولى لـ Go with Rob and Ken ، وانضم روبرت وروس إلى الفريق مبكرًا. لقد عملوا على Go منذ البداية. أعتقد أنه يمكننا الوثوق بهم لمعرفة ما إذا كان الاقتراح "يتعارض مع روح التعامل مع الخطأ كما قرره مؤلفو Go Authors الأصليون".

أنا لا أحب مبدأ الاقتراح الذي من شأنه أن يجمد معالجة الأخطاء كما هو اليوم. مثل هذا الاقتراح من شأنه أن يحظر جميع المقترحات المستقبلية بشأن هذا الموضوع.

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

طريقتنا في معالجة الخطأ فريدة من نوعها

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

المسار الذي نسير عليه حاليًا والذي لا يناسب اللغة أو النظام البيئي أو مستخدميها

عن ماذا تتحدث؟ Go يتحسن باستمرار. إنه لأمر مدهش أن تتمكن من الوصول إلى مثل هذه اللغة والأدوات والتوثيق الرائع مجانًا (كما هو الحال في حرية التعبير).

صممtheckman Robert التكرارات الأولى لـ Go with Rob and Ken ، وانضم روبرت وروس إلى الفريق مبكرًا. لقد عملوا على Go منذ البداية. أعتقد أنه يمكننا الوثوق بهم لمعرفة ما إذا كان الاقتراح "يتعارض مع روح التعامل مع الخطأ كما قرره مؤلفو Go Authors الأصليون".

أنا لا أحب مبدأ الاقتراح الذي من شأنه أن يجمد معالجة الأخطاء كما هو اليوم. مثل هذا الاقتراح من شأنه أن يحظر جميع المقترحات المستقبلية بشأن هذا الموضوع.

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

طريقتنا في معالجة الخطأ فريدة من نوعها

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

المسار الذي نسير عليه حاليًا والذي لا يناسب اللغة أو النظام البيئي أو مستخدميها

عن ماذا تتحدث؟ Go يتحسن باستمرار. إنه لأمر مدهش أن تتمكن من الوصول إلى مثل هذه اللغة والأدوات والتوثيق الرائع مجانًا (كما هو الحال في حرية التعبير).

يُظهر تاريخ تطوير Rust أن الرجال الذين يقفون وراءها لم يكن لديهم فكرة عما يفعلونه. قاموا بشكل أساسي بنسخ مبادئ معالجة الأخطاء من Haskell ، لكن هذه ليست مطابقة جيدة للبرمجة الضرورية (في العالم الحقيقي؟). الماكرو ? هو مجرد حل بديل لنظام معالجة الأخطاء الفاشل في البداية.

ianlancetaylor هناك اقتراح هنا. تقترح miekg أنك بصفتك أحد قادة لغتنا لم تعد تسعى لاستبدال if err != nil ببعض التركيبات الأخرى التي تتعارض مع روح التعامل مع الخطأ كما قرر مؤلفو Go الأصليون. بالنسبة لي شخصيًا ، يبدو أنك تحاول التأكيد على عدم أهمية هذا السؤال عن طريق نقله إلى golang-nuts بدلاً من التعامل معه مثل مقترحاتنا الأخرى. قد لا يكون هذا هو نيتك ، لكنه التأثير الذي أشعر به.

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

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

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

لا يمكنهم فعل أي شيء جاد مع نظام النوع الحالي من الستينيات. يحتاجون أخيرًا إلى استعارة أفكار الثمانينيات في Go 2.0

عن ماذا تتحدث؟ Go يتحسن باستمرار. إنه لأمر مدهش أن تتمكن من الوصول إلى مثل هذه اللغة والأدوات والتوثيق الرائع مجانًا (كما هو الحال في حرية التعبير).

ngrilly أن الجزء الأخير من المحتمل أن يكون لمناقشة أكبر. بدون إخراج هذا الاقتراح عن مساره ، ولكن مع وضع حد لهذا التعليق ، هناك شعور متزايد بعدم التوافق بين المستخدمين والقيادة في المجتمع / النظام البيئي.

بالنسبة لبقية المناقشة ، لا أعتقد أن إضافة المزيد من الجهد المعرفي إلى بناء الجملة هو فوز. أنا سعيد لأنهم وجدوا شيئًا يعمل معهم ، لسنا هم.

لقد فتحت للتو اقتراحًا لبيان if المضمّن: https://github.com/golang/go/issues/32860

المرجع: https://github.com/golang/go/issues/32825#issuecomment -506707539

إلى أي مدى سيصبح هذا العالم أجمل إذا قدم كل شخص اقتراحه حول أي ميزة جديدة من ميزات golang 2.0 التي يرغب في الحصول عليها ، سيوفر أيضًا فرعًا من مفترق طرق https://github.com/golang/go (وأي شيء آخر المستودعات الضرورية) التي تنفذ هذا الاقتراح.

لا توافق؟

@ av86743 يبدو خارج نطاق هذا الاقتراح. يرجى تقديم اقتراح يقترح مسار العمل هذا.

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

ماذا عن بناء الجملة هذا:

# call error handler
try callFunction(), errorHandler()

# error handler with anonymous function
variable := try callSomething(), func(err *Error) { # error handling }

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

نظرًا لأنك ذكرت "Go Authors" الأصلي ، أعتقد أنه من الجدير الإشارة إلى أن اقتراح "try" قدمه griesemer وهو أحد مؤلفي Go الأصليين الثلاثة.

أنا أتفق بشدة مع هذا الاقتراح ، وأعتقد أن الشيء الوحيد الذي يجب تغييره هو فقط go fmt ، و make go fmt للسماح بسطر واحد إذا كان البيان.

أنا حقا أريد سطر واحد من

if err != nil { return wrappedErr{err} }

بدلا من ثلاثة أسطر من

if err != nil {
    return wrappedErr{err}
}

@ av86743 يبدو خارج نطاق هذا الاقتراح. يرجى تقديم اقتراح يقترح مسار العمل هذا.

theckman أنت تخبرني ماذا أفعل ، وهذا ليس فقط غير مهذب ، إنه وقح ظاهري. يمكنك محاولة وضع نفسك بأي طريقة تختارها ، ولكن لا أنا ولا ، على الأرجح ، أي شخص آخر هنا هو قرد "go fetch" للقفز عندما تقول ذلك.

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

لن يكون سوى "جهد ضائع" من أجل [... _ الوصف بلغة مناسبة تمامًا تم حذفها من أجل الإيجاز_ ...].

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

@ av86743 أعتقد أن ما اقترحته هو فكرة مثيرة للاهتمام ولا أريد أن أضيع كتعليق في قضية غير ذات صلة. إذا لم تكن مهتمًا بمتابعتها بصفة رسمية للنظر فيها ، فأنا أعتذر عن دعوتك إلى إثارة قضية منفصلة.

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

لما يستحق الأمر ، فإن شكوكي الخاصة هي أن عملية تفكير فريق Go حول هذا الأمر قد ذهبت إلى شيء مثل:

  1. تحظى Go بشعبية كبيرة وتستخدم على نطاق واسع ، لذلك فإن آلاف الأشخاص لديهم ، بطبيعة الحال ، تعليقات واقتراحات وشكاوى بشأنها.
  2. يمكنك فقط المماطلة لفترة طويلة. يشعر فريق Go بضغط هائل للقيام _أمر ما _ حيال كل الضوضاء الصادرة عن المجتمع.
  3. هذا شيء.

ماذا عن إضافة وظيفة catch () في تأجيل للقبض إذا تم العثور على خطأ ما مثل الاسترداد ().
مثال:

func doSomthing()(err error){
    //return error
}

func main(){
    defer func(){
        if err:=catch();err!=nil{
            //found error
        }
    }()

    try doSomthing()
}

في العديد من الوظائف

func Foo() (err error) {
    defer func(){
        if err:=catch();err!=nil{
            //found error
        }
    }()

   try{
    file1 := open("file1")
    defer file1.Close()
    file2 := open("file2")
    defer file2.Close()
   }
   //without try
    file3,err := open("file3")
    defer file3.Close()
 }

ماذا عن إضافة وظيفة catch () في تأجيل للقبض إذا تم العثور على خطأ ما مثل الاسترداد ().
مثال:

func doSomthing()(err error){
    //return error
}

func main(){
    defer func(){
        if err:=catch();err!=nil{
            //found error
        }
    }()

    try doSomthing()
}

في العديد من الوظائف

func Foo() (err error) {
    defer func(){
        if err:=catch();err!=nil{
            //found error
        }
    }()

   try{
  file1 := open("file1")
  defer file1.Close()
  file2 := open("file2")
  defer file2.Close()
   }
   //without try
    file3,err := open("file3")
    defer file3.Close()
 }

كيف يساعد ذلك في معالجة كل خطأ بطريقة منفصلة؟

بعض التوضيحات:

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

  2. أتفق مع ianlancetaylor في أن هذه المناقشة تتم بشكل أفضل في مكان آخر (golang-nuts). لا يوجد اقتراح هنا.

  3. في الواقع ، bitfield ، ليس لدي أي "غضب داخلي حول الإسهاب في إرجاع الخطأ غير المغلف في Go" ، شكرًا لك :-) لكنني أعتقد أن التحقق من الأخطاء هو أكثر تفصيلاً وقد يكون ضروريًا ؛ وحقيقة أن نفس المشاعر قد أثارها المجتمع مرارًا وتكرارًا هي مؤشر واضح على أننا (فريق Go) لسنا وحدنا مع هذا الاعتقاد. لن أذهب إلى حد القول إن هناك الكثير من الضغط لفعل "شيء ما" - لقد عملنا على هذا الأمر وإيقافه لفترة طويلة الآن ، ونحن راضون تمامًا عن انتظار النهج "الصحيح" .

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

griesemer try عرضة للخطأ للغاية: لا يوجد RAII في Go ، لذلك لا يمكننا ترك الوظيفة في كثير من الحالات.

sirkon ، لست متأكدًا من علاقة RAII بهذه المناقشة. يستبدل try الأنماط الحالية لـ if ..., err := f(); err != nil { return ..., err } بـ ... := try(f()) . إذا كان هناك خطأ في تحرير الموارد باستخدام try ، فمن المؤكد أنه كان موجودًا مسبقًا أيضًا. لا يؤدي إدخال try تحسين أو منع خطأ تحرير المورد.

sirkon ، لست متأكدًا من علاقة RAII بهذه المناقشة. يستبدل try الأنماط الحالية لـ if ..., err := f(); err != nil { return ..., err } بـ ... := try(f()) . إذا كان هناك خطأ في تحرير الموارد باستخدام try ، فمن المؤكد أنه كان موجودًا مسبقًا أيضًا. لا يؤدي إدخال try تحسين أو منع خطأ تحرير المورد.

قراءة الموضوع ، كان هناك مثال:

info := try(try(os.Open(fileName)).Stat())

sirkon لقد رأيت هذا المثال عدة مرات الآن. أوافق على أنه مثير للاهتمام. لكن دعنا نفكر في الأمر أكثر قليلاً. try المدمج المقترح هو في الأساس سكر نحوي لنوع معين من النماذج المعيارية الموجودة في كود Go. لذا يمكننا تحويل هذا المثال إلى الكود الأصلي.

    f, err := os.Open(fileName)
    if err != nil {
        return err
    }
    info, err := f.Stat()
    if err != nil {
        return err
    }

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

[يبدو أن ianlancetaylor هزمتني للتو.]

sirkon هذا الخطأ ممكن بالفعل ، try أو لا - Go لا يمنعك من كتابة كود سيئ. أو تغيير هذا الأمر ، باستخدام رمز سيئ كسبب لعدم السماح باستخدام try ليس حجة مقنعة. بدلاً من ذلك ، يجب أن يشير go vet الحالات الإشكالية.

defer هو مصطلح Go للتنظيف عند إرجاع دالة ، وهذا يعمل بشكل جيد. النهج الصحيح هنا بالطبع سيكون:

f := try(os.Open(filename))
defer f.Close()
info := try(f.Stat())

قارن هذا بـ:

f, err := os.Open(filename)
if err != nil {
   return ..., err
}
defer f.Close()
info, err := f.Stat()
if err != nil {
   return ..., err
}

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

defer errd.Wrap(&err, "failed to do X for %s", filename)
f := try(os.Open(filename))
defer f.Close()
info := try(f.Stat())

باستخدام شيء مثل حزمة errd (راجع العدد رقم 32676).

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

defer errd.Wrap(&err, "failed to do X for %s", filename)
f, err:= os.Open(filename)
try(err) // check is so much a better term
defer f.Close()
info, err := f.Stat()
try(err)

أيضًا ، يبدو هذا الأسلوب الخاص بـ handle رائعًا هنا ، ولكن ألن يتسبب العديد من المؤجرين في حدوث فوضى؟ أم أنه سيكون هناك مزامنة ذات نطاق وظيفي. إذا كان الأمر كذلك ، فهل ستُمنح funcs المجهولة نطاقها الخاص؟ والتظليل ... إيش - من هو أولاً ، ما هو الثاني؟

كل هذا يبدو وكأنه سيكون هناك "وضعان" لكتابة كود Go. قل أنه ليس كذلك.

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

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

وهو ما يقودني إلى سبب اختلافي بشدة مع العبارة "يمكنك فعل ذلك اليوم" - لأنك ببساطة لا تستطيع ذلك. بالتأكيد - يمكنك تسريب مقبض الملف. لكن Go لا يمنح المبرمج الفرصة لتخطي وجود المعرف في النطاق وبالتالي تسريب الملف أيضًا. كل معرف f تخطيه هو مقبض ملف تم تسريبه. يتطلب استخدام الميزة _تطلب_ تعطيل بعض التعابير البارزة في نظام Go. وبالتالي ، فإن تقديم الميزة كما تم تصميمها اليوم يزيد بشكل واضح من خطر تسريب الموارد في Go.

ومع ذلك ، كما ذكرت في https://github.com/golang/go/issues/32825#issuecomment -506882164 سأدعم try إذا تم إجراء تعديل صغير ، أعتقد أن التغيير سيكون رحب بالإضافة إلى اللغة. كل ما أعتقد أنه يحتاج إلى المحاولة هو جعله صالحًا فقط على RHS للمهمة وعدم السماح لقيمة الإرجاع بأن تكون قابلة للعنونة. اجعل الأمثلة "الجيدة" لاستخدام المحاولة (تميل إلى أن تكون تجربة واحدة لكل سطر) هي الطريقة "الوحيدة" لاستخدام المحاولة ، على سبيل المثال:

info := try(try(os.Open(filename)).Stat()) // compiler error
f := try(os.Open(filename)) // valid
// we were forced to assign f, so we still have an identifier to Close (serve linters and users alike)
defer f.Close()
info := try(f.Stat())
a, b := try(strconv.Atoi("1")), try(strconv.Atoi("2")) // also valid 
a, b := try(strconv.Atoi("1"), strconv.Atoi("2")) // maybe?
a, b := try strconv.Atoi("1"), strconv.Atoi("2")

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

total := try(try(os.Open(filename)).Stat()).Size() + try(strconv.Atoi(try(ioutil.ReadAll(os.Stdin))))

لكن _سوف نفكر في الأسوأ ، إذا سمحت لنا بذلك.

daved يتم دعم وضع try(err) في السطر الثاني بشكل كامل مع عرض try : try ببساطة يريد وسيطة يتم تقييمها إلى قيمة واحدة أو أكثر حيث تكون القيمة الأخيرة هي اكتب error ، وهو راضٍ بشكل طبيعي عندما تكتب try(err) .

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

cstockton أوافق على أن المتداخلة try يمكن أن تكون مشكلة كبيرة ؛ لكنني أعتقد أيضًا أنه إذا كان لدينا try ، فإن معظم الشفرة ستبدو مثل الأمثلة (الصالحة)

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

griesemer شكرا للتوضيح. في مثال الكود ، إذا كان السطر الأول var err error ، فهل من المحتمل أن يؤثر الالتفاف على كلا الأخطاء المحددة؟ لقد رأيت الحديث عن التظليل باعتباره مصدر قلق يمكن التعامل معه في المستقبل. كيف / قد يرتبط ذلك بهذا؟

ماذا عن إضافة وظيفة catch () في تأجيل للقبض إذا تم العثور على خطأ ما مثل الاسترداد ().
مثال:

func doSomthing()(err error){
    //return error
}

func main(){
    defer func(){
        if err:=catch();err!=nil{
            //found error
        }
    }()

    try doSomthing()
}

في العديد من الوظائف

func Foo() (err error) {
    defer func(){
        if err:=catch();err!=nil{
            //found error
        }
    }()

   try{
    file1 := open("file1")
    defer file1.Close()
    file2 := open("file2")
    defer file2.Close()
   }
   //without try
    file3,err := open("file3")
    defer file3.Close()
 }

كيف يساعد ذلك في معالجة كل خطأ بطريقة منفصلة؟

مثل المستخدمين الآخرين الملتزمين

func Foo() (err error) {
    defer func(){
        if err:=catch();err!=nil{
            //found error
        }
    }()

    file1 :=try open("file1")
    defer file1.Close()
    file2 :=try open("file2")
    defer file2.Close()

        //without try
       file3,err := open("file3")
       defer file3.Close()
 }

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

func f(...) (..., err error) {
   var err error // << err already declared
   ...

من ناحية أخرى ، إذا كتبت:

func f(...) (..., err error) {
   a, b, ... err := g() // redeclaration of err
   ...

err في المهمة هو ببساطة نفس الاسم المسمى في قائمة معلمات النتائج. لا يوجد شيء مختلف هنا عما كان عليه الحال بالفعل لفترة طويلة جدًا.

ملاحظة: من المحتمل أن نتوقف عن اختطاف هذه المشكلة لمناقشات try والعودة إلى الاقتراح الأصلي - سيتم فتحه وفتحه للمناقشة غدًا (1 يوليو) مرة أخرى.

godcong A catch() دالة (أو ما شابه) ستسمح لك فقط بالحصول على الخطأ ، وليس لتعيينه (وعادة ما يريد المرء تعيين خطأ وظيفة التضمين في دالة مؤجلة تعمل كمعالج للأخطاء) . يمكن جعله يعمل بجعل catch() يُرجع *error وهو عنوان قيمة إرجاع خطأ دالة التضمين. ولكن لماذا لا تستخدم اسم نتيجة الخطأ فقط بدلاً من إضافة آلية جديدة إلى اللغة؟ راجع أيضًا عرض try حيث تتم مناقشة ذلك.

أيضًا ، راجع PS أعلاه .

تضمين التغريدة

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

أعتقد أن هذا هو بالضبط الخلل الفادح للاقتراح try() : حيث كان هناك من قبل طريقة واحدة فقط للقيام بفحص الأخطاء ، الآن سيكون هناك طريقتان ، متداخلة من خلال قاعدة الكود. أيضًا ، على الأقل بالنسبة لقاعدة الشفرة التي أعمل عليها ، يمكن استبدال أقل من 20٪ من if err != nil بـ try() ، والتي على الرغم من أنها ليست تافهة ، لا يبدو أنها تستحق بينما تكفي لإنشاء الانقسام في أنماط معالجة الخطأ.

أنا شخصياً كنت أفضل بناء معالجة الأخطاء الذي يكون قويًا بما يكفي لاستبدال 95 ٪ من جميع الحالات if err != nil بدلاً من ذلك. أعتقد أن هذا هو ما كان كثير من الناس سيحبونه أيضًا.

griesemer أوافق على أن الناس سيتعلمون وأن الأدوات ستكون

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

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

sirkon لقد رأيت هذا المثال عدة مرات الآن. أوافق على أنه مثير للاهتمام. لكن دعنا نفكر في الأمر أكثر قليلاً. try المدمج المقترح هو في الأساس سكر نحوي لنوع معين من النماذج المعيارية الموجودة في كود Go. لذا يمكننا تحويل هذا المثال إلى الكود الأصلي.

    f, err := os.Open(fileName)
    if err != nil {
        return err
    }
    info, err := f.Stat()
    if err != nil {
        return err
    }

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

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

godcong A catch() دالة (أو ما شابه) ستسمح لك فقط بالحصول على الخطأ ، وليس لتعيينه (وعادة ما يريد المرء تعيين خطأ وظيفة التضمين في دالة مؤجلة تعمل كمعالج للأخطاء) . يمكن جعله يعمل بجعل catch() يُرجع *error وهو عنوان قيمة إرجاع خطأ دالة التضمين. ولكن لماذا لا تستخدم اسم نتيجة الخطأ فقط بدلاً من إضافة آلية جديدة إلى اللغة؟ راجع أيضًا عرض try حيث تتم مناقشة ذلك.

أيضًا ، راجع PS أعلاه .

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

سيكون من المثير للاهتمام معرفة إلى أي مدى سيتم استخدام مرافق الأخطاء الجديدة والمحسّنة في golang/go نفسها. إذا كان على كل حال.

سيكون من المثير للاهتمام أيضًا معرفة ما إذا كان سيكون لدى go2 fmt خيار إخراج عادي go1.x .

من تجربتي الخاصة ، منذ أن أضفت السياق في الخطأ الذي أعيدته من خلال:

import "github.com/pkg/errors"
func caller(arg string) error {
  val, err := callee(arg)
  if err != nil {
    return errors.Warpf(err, "failed to do something with %s", arg)
  }

  err = anotherCallee(val)
  if err != nil {
    return errors.Warpf(err, "failed to do something with %s", val)
  }

  return nil
}

نادراً ما يحتاج فريق الدعم إلى مدخلاتي للمشكلات التي تنشأ في الإنتاج.

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

ربما أضف سياقًا لتأجيله:

func caller(arg string) (err error) {
  defer func() {
    switch t := err.(type) {
      case CalleeErr:
        err = errors.Wrapf(err, "failed to do something with %s", arg)
      case AnotherCalleeErr:
        err = errors.Wrapf(err, "failed to do something with %s", val)
    }
  }()

  val := try(callee(arg))
  try(anotherCallee(val)
  return nil
}

لا يبدو أنه يوفر الكثير من الكتابة ، لكننا نضحي بقابلية القراءة والأداء.

قد ينتهي بك الأمر باستخدام try () بهذه الطريقة:

func caller(arg string) error {
    val, err := callee(arg)
    try(errors.Warpf(err, "failed to do something with %s", arg))

    err = anotherCallee(val)
    try(errors.Warpf(err, "failed to do something with %s", val))

    return nil
  }

إنها تقلل بضعة أسطر ، هذا كل شيء.

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

على الرغم من أنني سأحبها إذا كان بإمكاننا الحصول على حالة خاصة if و return هذا يشبه نوعًا الياقوت

return err if err != nil

ملاحظة: عفوا قلة خبرتي في تصميم اللغة ، إذا قلت شيئًا غبيًا ، فلا تتردد في الإشارة إليه وتثقيفي.

ماذا عن إضافة وظيفة catch () في تأجيل للقبض إذا تم العثور على خطأ ما مثل الاسترداد ().
مثال:

func doSomthing()(err error){
    //return error
}

func main(){
    defer func(){
        if err:=catch();err!=nil{
            //found error
        }
    }()

    try doSomthing()
}

في العديد من الوظائف

func Foo() (err error) {
    defer func(){
        if err:=catch();err!=nil{
            //found error
        }
    }()

   try{
  file1 := open("file1")
  defer file1.Close()
  file2 := open("file2")
  defer file2.Close()
   }
   //without try
    file3,err := open("file3")
    defer file3.Close()
 }

كيف يساعد ذلك في معالجة كل خطأ بطريقة منفصلة؟

مثل المستخدمين الآخرين الملتزمين

func Foo() (err error) {
    defer func(){
        if err:=catch();err!=nil{
            //found error
        }
    }()

  file1 :=try open("file1")
  defer file1.Close()
  file2 :=try open("file2")
  defer file2.Close()

        //without try
       file3,err := open("file3")
       defer file3.Close()
 }

في المثال الخاص بك أنت تتعامل مع جميع الأخطاء من قبل نفس ديففر. ماذا يحدث إذا كنت تريد إضافة رسالة مخصصة ومعلومات مخصصة إلى الخطأ؟

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

  • try كاسم للميزة مثيرًا للجدل ، طالما أننا ننفذ هذا كوظيفة ، فإننا عالقون في إعطائها اسمًا لست متأكدًا من أن أي شخص سيشعر بالرضا تجاهه بنسبة 100٪.
  • try بعدد غير مسبوق من الأشياء ، وله مدخلات مثل append() ويؤثر على تدفق التحكم مثل panic() بينما يحل محل نمط واسع الانتشار ( if err != nil ) .
  • تداخل try هو نتيجة لقرار تنفيذه كوظيفة ، وليس كقيمة مضافة مشتقة من نقاش تقني صارم.
  • try كدالة للحفاظ على التوافق مع الإصدارات السابقة

أعتقد أنه إذا أردنا ببساطة استنتاج العوائد مثلما نفعل في الأنواع ، فإن الأشياء تبدو موجزة وتشعر بمزيد من "الانتقال" مثل:

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

info, err := f.Stat()
if err != nil {
   return ..., err
}

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

f := os.Open(filename)
defer f.Close()
info := f.Stat()

الذي يبدو مرتبًا أكثر بكثير من:

f := try(os.Open(filename))
defer f.Close()
info := try(f.Stat())

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

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

في هذا المثال ، يتم تقييم حالة on كل مرة يتم فيها تعيين err .

func example() (foo int, err error) {
    on err != nil {
        return foo, err
    }

    foo, err = calcThis()
    foo, err = calcThat(foo)

    return
}

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

func example() (*int, error) {
    var err error

    on err != nil {
        return nil, err
    }

    foo, err = calcThis()
    foo, err = calcThat(&foo)

    return &foo, nil
}

يمكن أيضًا تعيين هذا عدة مرات. إليك مثال مفتعل:

func example() (*int, error) {
    var err error

    on err != nil {
        return nil, err
    }

    foo, err = calcThis()

    on err != nil {
        return &foo, err
    }

    foo, err = calcThat(&foo)

    return
}

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

يمكنك حتى القيام بما يلي:

func example() (foo int, err error) {
    var message string

    on err != nil {
        return foo, errors.Wrap(err, message)
    }

    message = "failed to calc this"
    foo, err = calcThis()

    message = "failed to calc that"
    foo, err = calcThat(foo)

    return
}

أخيرًا ، هذا له قابلية للتطبيق تتجاوز معالجة الأخطاء. تريد العودة إذا foo == 0؟ مثال آخر مفتعل:

func example(i int) bool {
    on x == 0 {
        return false
    }

    x = calcSomeInt(i)
    return true
}

تضمين التغريدة

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

  • هذا بالفعل "مسكتك" مع وظائف ترجع أخطاء _ فقط_ ، مثل json.Unmarshal . يتعلم مراجعو التعليمات البرمجية الجيدة لمحرر أسلوب الإدخال (IME) البحث عن هذا بسرعة كبيرة.
  • سيظل خطأ في بناء الجملة في الوظائف التي لا تُرجع قيم الخطأ ، مثل طرق http.Handler .
  • إذا كنت تعرف كيفية التعامل مع الخطأ ، فستفكر فيه ، وستتوقعه ، ومن المحتمل أن تحصل على قيمة الخطأ على أي حال - حتى تتمكن من التعامل معه.
  • إذا كانت نيتك هي تجاهل أي أخطاء واجهتها لجعلها مشكلة شخص آخر (المتصل) ، فإن هذا يحقق ذلك ، مع الجانب السلبي الصغير الذي يفوتك فرصة لمعالجة المزيد من التعليقات التوضيحية بشكل صريح للسياق.

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

آمل أن تكون هذه هي الطريقة الصحيحة للرد ولا ألتزم بـ
زائف خطيرة.

في 7/1/19 ، كتب Chris Stockton [email protected] :

ianlancetaylor هل اقترح أي شخص زيادة عامل التشغيل ": =" للدعم
"العوائد المستنبطة" - تتصرف بشكل أساسي كما حاولت بدون وظيفة
مكالمة. [...]

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

لوسيو.

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

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

data := try(ioutil.ReadAll(try(os.Open("foo.txt"))))

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

data := try(ioutil.ReadAll(try(http.Get("http://example.com/")).Body))

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

بصفتي شخصًا يراجع أكثر من يكتب رمزًا في هذه المرحلة ، أفضل ألا تضيف Go ميزات تجعل الخطأ مغريًا للغاية.

@ jesse-amano إن استخدام عامل الإسناد يمنع في الواقع هذه الحالة من أن تكون ممكنة ، بدون بيان تخصيص صريح ، يتصرف أدناه تمامًا كما يفعل اليوم ، على سبيل المثال:

json.Unmarshal(...)
(http.Handler)(h).ServeHTTP(w, r)

بالنسبة للقيم التي تُرجع خطأً للتو ، فهي مؤهلة لإعادتها مثل return json.Unmarshal(...) ويمكن أيضًا تمثيلها في شكل أكثر إحكاما حيث لا توجد حاجة لوجود قيمة خارج كتلة if.

if err := json.Unmarshal(...); err != nIl {
    return err
} 

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

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

تحرير: @ جيسي أمانو فاتني تماما وجهة نظرك آسف! أفترض أن التواجد داخل دالة لا تُرجع خطأً سيعرض خطأ تجميع غير متطابق في عدد المهام النموذجية؟ أتخيل أن المحاولة ستقدم على الأرجح نوعًا جديدًا من رسائل خطأ المترجم لذلك.

beoran بخصوص https://github.com/golang/go/issues/32825#issuecomment -507126700: معالجة الأخطاء تختلف بالفعل من موقف إلى آخر: في بعض الأحيان نعيد الخطأ دون تغيير ، وأحيانًا نرجع خطأ مزينًا ، وأحيانًا نتصرف عند حدوث خطأ ، وأحيانًا (يمكننا بشكل صحيح) تجاهل الخطأ. الشيء الوحيد الذي يشاركونه جميعًا (باستثناء عندما نتجاهل الخطأ) هو اختبار err != nil (والذي يمكننا القيام به بالفعل بأكثر من طريقة واحدة). بقدر ما سيكون من الجيد أن تلتقط ميزة اللغة الجديدة كل هذه الحالات (أو 95٪ منها) ، فمن المرجح جدًا أن تتدخل مثل هذه البنية بطرق غير متعامدة مع بنيات التحكم الأخرى التي لدينا بالفعل. وهذا يعني أن أفضل ما يمكن أن نأمله هو جعل بعض هذه الحالات (ربما 20٪ ، وربما 50٪ منها) أفضل.

cstockton بخصوص https://github.com/golang/go/issues/32825#issuecomment -507136111: إذا كانت متداخلة try هي الكتلة الوحيدة المتبقية و go vet ليست جيدة بما فيه الكفاية ، أعتقد أنه يمكننا التفكير في عدم السماح ب try 's المتداخلة - سيكون ذلك سهلاً بدرجة كافية. لكن في الوقت الحالي ، أعتقد أننا ما زلنا في مرحلة الخوف وعدم اليقين والشك) ، حيث لم يقم أحد عمليًا بتجربة try محمل الجد. ألاحظ أن الأشخاص الذين فعلوا ذلك ، أفادوا بشكل إيجابي.

ملاحظة: تم فتح إصدار try مرة أخرى للتعليق. لنكمل هناك.

cstockton أوه ، json.Unmarshal دون تسجيل قيمة الخطأ في معظم الحالات ، ولكن عادةً لا تعتبر ممارسة سيئة لـ defer file.Close() بدلاً من ذلك من defer func() { err = file.Close(); if err != nil { ... }; }() ، وتعلمنا معرفة الفرق بسهولة تامة. لذلك يجب (على الأرجح) أن يكون مع التغيير الذي اقترحته. لقد تراجعت في البداية عن فكرة أن شخصًا ما يستخدم ببراءة foo := strconv.ParseFloat(bar, 64) عندما كان ينوي التعامل مع الخطأ ، ولكن بعد دراسة موجزة ، لا أعتقد حقًا أنها مشكلة بعد كل شيء.

sirkon بخصوص https://github.com/golang/go/issues/32825#issuecomment -507167388: يرجى ترك مثل هذه البيانات غير المؤهلة بوضوح خارج المناقشة - ليس لها مكان هنا. فقط للتسجيل ، العديد من الأفكار في Go هي في الواقع من الثمانينيات (حزم ، تجميع منفصل ، إلخ) بدلاً من الستينيات والعديد منها أصغر كثيرًا (goroutines ، واجهات). قد تبدو هذه الأفكار قديمة ، لكنها صمدت أمام اختبار الزمن (على الأقل الوقت القليل الذي كانت فيه CS موجودة).

turtleDev بخصوص https://github.com/golang/go/issues/32825#issuecomment -507231353: Go يقوم بمعالجة الاستثناءات ، ويتم ذلك باستخدام panic و defer و recover - نحن لا نسميها "معالجة الاستثناءات" لأن هذا المصطلح يأتي مع دلالة مضللة لـ Go. ولكن للتوضيح فقط ، يمكن لـ Go أن يفعل كل ما يمكن أن يفعله raise مع try-catch ، وأكثر (لأنه على عكس try-catch ، يمكن استخدام defer بشروط). شكرا.

sorenvonsarvort بخصوص https://github.com/golang/go/issues/32825#issuecomment -507268293: إذا كنت تريد زخرفة خطأ مختلفة لكل حالة ، فاستخدم العبارة if . يرجى الاطلاع على مستند التصميم. تم الرد على هذا السؤال عدة مرات حتى الآن. شكرا.

cstockton بخصوص https://github.com/golang/go/issues/32825#issuecomment -507306652: نعم ، لقد فكرنا في مثل هذه الآلية. على وجه التحديد ، فكرنا في "قلب الجداول" وبدلاً من تقديم try ، ما عليك سوى تقديم handle ، والذي يعلن عن معالج خطأ. إذا كان المعالج موجودًا (وعندها فقط) ، فسيترك الشخص ببساطة err في LHS للمهمة ، وسيتم التحقق منه ضمنيًا (كما هو الحال مع try غير مرئي). يبدو الأمر جيدًا ، لكنه أيضًا غير مرئي تمامًا ، وهو علم أحمر كبير. نريد أن تكون معالجة الاستثناءات مرئية بوضوح في الكود ، في كل مكان. بدون ذلك ، سيكون من المستحيل تقريبًا قراءة التعليمات البرمجية ومعرفة مكان حدوث فحص الأخطاء.

griesemer شكرا

@ Matthew-noken بخصوص https://github.com/golang/go/issues/32825#issuecomment -507323973: أنت تقترح فكرة مثيرة للاهتمام ؛ يشبه إلى حد كبير آلية نقطة المراقبة الخاصة بمصحح الأخطاء. هناك بعض الأسئلة التي يجب الإجابة عليها: هل يجب أن تعود الكتلة on (أظن ذلك ، لأنك بخلاف ذلك ستدخل إلى أرض المعكرونة)؟ هل يمكن للمرء الحصول على أكثر من كشف حساب on ؟ ما مدى تعقيد شرط on (يجب تقييمه عند كل مهمة)؟ لاحظ أنه لا يمكننا الحصول على تعبيرات عشوائية لأنه يتعين علينا تحديد المتغير بشكل فريد باستخدام عبارة on . أيضًا ، إلى حد ما لعنة في Go: يشير الإنشاء on إلى رمز غير مرئي ليتم تنفيذه في مكان آخر.

إذا كنت ترغب في استكشاف هذا أكثر ، أقترح مناقشة هذا في مكان آخر (golang-nuts ، أو اقتراح جديد مختلف). شكرا.

@ كما بخصوص https://github.com/golang/go/issues/32825#issuecomment -507345821:

تم استبعاد حساب المؤشر من Go على أساس أنه يجعل من السهل كتابة تعليمات برمجية سيئة ومعطلة

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

لا توجد خبرة ولا دليل حتى الآن على أن المتداخلة try ستكون سائدة أو شائعة. لكن راجع https://github.com/golang/go/issues/32825#issuecomment -507358397.

turtleDev بخصوص https://github.com/golang/go/issues/32825#issuecomment -507368167: A panic _exactly_ استثناء ، و recover داخل دالة مؤجلة هي في الأساس catch . قد يكون من الصعب العثور عليها في كود الإنتاج لأننا في Go لا نوصيك بكتابة الكود الخاص بك باستخدام استثناءات ؛ يجب استخدامها فقط في ظروف استثنائية.

فيما يتعلق بعدد هياكل تدفق التحكم: الاقتراح واضح جدًا أن try هو مجرد سكر نحوي ، لا شيء آخر.

لقد حاولت الرد على بعض التعليقات الأخيرة في هذا الموضوع على هذا الموضوع. ولكن دعونا لا تزال تعليقات جديدة على try الاقتراح الفعلي try قضية # 32437 (مقفلة الآن مرة أخرى اعتبارا من اليوم). واترك هذه المشكلة محجوزة للمناقشة leave err != nil alone . شكرا.

cstockton تعليق آخر حول https://github.com/golang/go/issues/32825#issuecomment -507306652: إذا قمنا بتنفيذ هذا ، فابدأ بـ

    func f() int { ... }
    ...
    x := f()

والتغيير إلى

    func f() (int, error) { ... }

قد يعني أن سلوك x := f() سيختلف تمامًا فجأة وبصمت.

أجريت بعض التجارب المشابهة لما فعلته lpar على جميع مستودعات go. النتائج في هذا المحتوى: https://gist.github.com/freeformz/55abbe5da61a28ab94dbb662bfc7f763

ianlancetaylor أشعر في الواقع أن هذا

func f() int { ... }
func a() error {
    x := f() // if f() is updated to return an error, we get automatic error propagation by default
    ...
}

func b() {
    x := f() // if f() is updated to return an error, we get the same compiler error 
    // assignment mismatch: 1 variable but pkg.f returns 2 values
}

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

func (t *T) a() error {
   t.mu.Lock()
   x := f() // if f() is updated to return an error, we get automatic error propagation by default
   if x > 15 {
     t.mu.Unlock()
     return errors.New("t > 15")
   }
   ...
}

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

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

تضمين التغريدة

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

اعذرني ، لكن لن يكون try ماكروًا (مثل C) ، لذا في الواقع ، بالنسبة للمستخدم النهائي ، فهو مجرد عنصر تحكم آخر في بيان التدفق.

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

مرة أخرى ، هذه هي آرائي وأنا أمثلهم فقط. أنا متأكد من أن فريق Go قد فكر في هذا الأمر أكثر مما أفكر في أي وقت مضى.

الجانب: يبدو لي غريبًا أن هذه المشكلة بها 1335 صوتًا مؤيدًا بينما اقتراح try (# 32437) يحتوي فقط على 279 تصويتًا معارضًا. أتوقع أن يقوم الأشخاص الذين يؤيدون هذا التصويت بالتخفيض على اقتراح try بحيث تكون مشاعر المجتمع حيال ذلك أكثر وضوحًا ، لأن هذين الاقتراحين متنافيان.

تضمين التغريدة

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

متفق عليه هناك ، أن الكثير واضح.

الشيء الوحيد الذي يشاركونه جميعًا (باستثناء عندما نتجاهل الخطأ) هو اختبار err != nil (والذي يمكننا القيام به بالفعل بأكثر من طريقة واحدة). بقدر ما سيكون من الجيد أن تلتقط ميزة اللغة الجديدة كل هذه الحالات (أو 95٪ منها) ، فمن المرجح جدًا أن تتدخل مثل هذه البنية بطرق غير متعامدة مع بنيات التحكم الأخرى التي لدينا بالفعل. وهذا يعني أن أفضل ما يمكن أن نأمله هو جعل بعض هذه الحالات (ربما 20٪ ، وربما 50٪ منها) أفضل.

العبارة try() المقترحة أيضًا "تتداخل" مع if و return بطرق غير متعامدة ، لذلك أقول إن هذه الوسيطة غير صحيحة. بعض الناس هنا يكرهون try() لهذا السبب بالذات ، لكنني لا أوافق. Go ليس أوبيرون ، إنه بسيط ولكنه ليس أضيق الحدود ، Go هو أكثر عملية.

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

beoran ،

لقد كتبت أنك ترغب في الحصول على "أكثر قوة" ، "أكثر فائدة بشكل عام" try() .

ذكرت griesemer 4 حالات:

  1. أعد الخطأ دون تغيير
  2. إرجاع خطأ مزخرف
  3. التصرف بناء على خطأ
  4. تجاهل الخطأ

try() يحل 1 حسب التصميم: إنه حرفياً اختصار لـ if err != nil { return ..., err } .

حل تراكيب اللغة الحالية 3 و 4. يمكننا بالفعل التصرف عند حدوث خطأ بـ if err != nil { ... } وسيكون من الصعب العثور على بنية أكثر إيجازًا في هذه الحالة. يمكننا بالفعل تجاهل خطأ في _ .

هذا يترك لنا 2 (إرجاع خطأ مزين). يقترح اقتراح try() استخدام عبارة تأجيل لتزيين الخطأ ، أو إذا كان يجب تزيين كل خطأ بشكل مختلف ، فاستخدم بنية قياسية if err != nil { ... } .

تم شرح المنطق جيدًا في هذا الجزء من الاقتراح :

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

handler := func(err error) error {
        return fmt.Errorf("foo failed: %v", err)  // wrap error
}

f := try(os.Open(filename), handler)              // handler will be called in error case

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

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

[...]

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

هل تختلف مع هذا المنطق؟

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

علاوة على ذلك ، نتعامل الآن مع جميع حالات استخدام الأخطاء الأربع ببيان if err != nil ، وهو مفهوم على نطاق واسع ، ومتسق في Go idiom. فقط باستخدام try() للحالة 1 ، وربما للحالة 2 ، إذا كنا لا نمانع في تنفيذ التفاف الأخطاء في عبارة مؤجلة ، فهذا يعني أن رمز معالجة الأخطاء سيتم تقسيمه إلى if و try غير متسق ، وإذا تغيرت معالجة الخطأ ، فسيتعين علينا التبديل بين أحدهما والآخر.

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

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

beoran أعتقد أن الحالتين 1 و 2

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

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

بكل جدية ، إذا كنت الشخص الذي يقف وراء اتخاذ هذه القرارات ، أود حقًا إعادة توظيف عامل تشغيل Kotlin ? أو شيء مثل الصدأ unwrap() سلوك.

أعتقد أن أيًا من هذه الأشياء سيكون بمثابة تحسن:

getFile()?.getPath()?.toString()

حيث تحصل على nil مرة أخرى إذا كان هناك خطأ على طول الطريق أو

get_file().unwrap().get_path().unwrap().lossy_to_string()

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

في 7/2/19 ، كتب Nicolas Grilly [email protected] :

beoran أعتقد أن الحالتين 1 و 2
الوظيفة (على التوالي بدون الزخرفة ومع الزخرفة) ، في حين أن الحالتين 3 و 4
لا (تتصرف بناءً على خطأ وتجاهل خطأ على التوالي). أعتقد 'جرب () `
التركيز على الحالتين 1 و 2.

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

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

في رأيي ، يتم تقديم الحالتين 1 و 2 بشكل أفضل من خلال أمر "فشل"
الكلمة الرئيسية التي تشير بوضوح (بعد أن يعتاد البعض عليها) أ
التغيير في تدفق البرنامج والذي لا يخضع لأي تغيير
تقليد مزعج فيما يتعلق بصياغته الكاملة.

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

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

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

lootch هل لديك قلق خاص بشأن الخطأ الذي يلزم أن يكون آخر try للعمل؟ أليس هذا بالفعل اصطلاح واقعي؟

(جانبا ، نحن لا نتأذى من المقاومة - معظمنا مندهش من رد الفعل الهائل).

griesemer آسف

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

لقد انزعجت بما فيه الكفاية من هذا منذ بضع سنوات ، لقد كتبت بالفعل بعض السكر حول الذعر واستعدت للسماح لهم بالعمل (في الغالب) مثل الاستثناءات التي لم يتم التحقق منها: https://hackthology.com/exceptions-for-go-as-a-library. أتش تي أم أل . في الواقع ، يعد استخدام هذه الأغلفة فكرة سيئة بسبب مكان وجود المجتمع. ومع ذلك ، ما زلت أعتقد أن تحسين بيئة العمل حول معالجة الأخطاء هو فوز.

يبدو من الجنون أن الناس يتجادلون بحماسة من أجل الحفاظ على طريقة مطولة للغاية ولكنها غير مفيدة لنشر ظروف الخطأ. لقد كتبت ووجدت أخطاءً حقيقية في الكود الخاص بي (وكود الآخرين) حيث أفسد الأشخاص حالة if err != nil وقاموا بإنشاء أخطاء. لا يوجد سبب حقيقي لكتابة هذه الأخطاء على الإطلاق. يمكن أن يساعد الفحص الثابت ولكن لا يقضي على هذه الأخطاء.

أنا أؤيد المقترحات لتحسين بيئة العمل حول معالجة الأخطاء.

عملية الاقتراح هي ، على سبيل الاقتباس https://golang.org/s/proposal ، "اقتراح التغييرات للذهاب".
هذه المشكلة لا تقترح تغييرًا ، لذا فهي في الحقيقة نوع من الخطأ في الفئة.
إذا قدم شخص ما عرضًا بعنوان "اترك التحديد بمفرده" ، فسنغلقه باعتباره ليس اقتراحًا.

لكن في الحقيقة هذه المسألة هي مجرد امتداد لمناقشة قضايا أخرى مثل # 32437 ، لذا سأتركها مفتوحة كمكان جيد لمناقشة "عدم القيام بأي شيء".

لقد قمت بوضع علامة تعليق على اقتراح للإشارة إلى أنه لا يوجد قرار محدد هنا ، أكثر من قرار شامل.

الجانب: يبدو لي غريبًا أن هذه المشكلة بها 1335 صوتًا مؤيدًا بينما الاقتراح try (# 32437) لديه _ فقط_ 279 تصويتًا معارضًا.

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

في 7/2/19 ، كتب Robert Griesemer [email protected] :

lootch هل لديك قلق معين بشأن الخطأ الذي يجب أن يكون الأخير
عودة المعلمة try للعمل؟ أليس هذا بالفعل أمر واقع
اتفاقية. معاهدة؟

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

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

أنا مستخدم متردد لبعض حيل Go الأكثر ذكاءً (لدي
مذكور في مكان آخر أن x ++ و x - لا يتميزان - في الغالب ، أخطأت
في بعض الأحيان - في الكود الخاص بي) لذلك ستظل كلمة "try" واحدة من هؤلاء بالنسبة لي ،
لكنني لا أعترض من حيث المبدأ على مقدمته ، أشعر فقط
لقد قيل الكثير ، ومع ذلك فإن كل الجوانب السلبية المحتملة قد لا تكون كذلك
تم الكشف عنها (العواقب غير المقصودة لما أراه سيصبح
قرار سياسي نهائي). قد يكون وضع ذلك على المحك أمرًا جيدًا
او سيء.

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

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

الأهم من ذلك كله ، أعتقد أن الجبل الذي يتكون من هذا التراب هو
دلالة على أن Go قد وصلت إلى مرحلة مغيرة للحياة. تقريبيا
بالتأكيد ، الافتراضات التي كانت صحيحة في عام 2013 ، ربما لم تعد كذلك
معلق. ليس هذا خبراً لأي شخص ، لكنه يشير إلى أن Go2 قد تفعل ذلك
ليس من الرائع أن تكون متوافقًا مع الإصدارات السابقة مع Go1 كما كان
مقترح بحزم (في رأيي).

lootch شكرا

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

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

شكرا مرة أخرى على المدخلات الخاصة بك. هذا كله ممتع جدا

لست متأكدًا مما إذا كان هذا الاقتراح هو المكان المناسب لمناقشته ، ولكن نظرًا لوجود مناقشة حية بالفعل حول الكلمة الأساسية التجريبية في هذا الموضوع ، سأعتبره موضوعًا في الوقت الحالي :)

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

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

إذا كنت ستكتب دراسة حالة عن كيفية استخدام الكلمة الأساسية "جرب" _ في الممارسة_ والقيمة المضافة إليها في الحياة الواقعية ، فقد يوفر ذلك حالة مقنعة. إذا لم يقدم (افتراضيًا) أي قيمة حقيقية في العالم ، فسيكون من المفيد معرفة ذلك أيضًا. في بعض الأحيان ، يبدو شيء ما جيدًا حقًا على الورق ولكنه لا ينجح في الممارسة - أو العكس.

Freeaqingme لقد أنتجنا بالفعل CL مؤقتًا يوضح كيف قد يبدو try في مكتبة الأمراض المنقولة جنسياً: https://go-review.googlesource.com/c/go/+/182717 (قد يكون هناك خطأ الإيجابيات ، ولكن إذا كانت موجودة ، فهي نادرة جدًا). نحن نفكر ربما في تطوير أداة تسمح بتحويل قواعد الكود في كلا الاتجاهين.

نشجعك أيضًا على استخدام tryhard في قاعدة الشفرة الخاصة بك وإعادة الإبلاغ.

شكرا.

griesemer ربما لم أوضح نفسي. أفترض أن Google تستخدم تصميم Go داخليًا. اقتراحي هو تطبيق التصحيح الذي ربطته أعلاه على بنية Google الداخلية ، ثم تحويل (أجزاء) من قاعدة شفرة Google - ليس فقط stdlib ، ولكن بشكل خاص المشاريع الداخلية المكتوبة في Go. إذا كان مهندسو Google سيستخدمونها لمدة شهرين في مواقف الحياة الواقعية ، فإن ذلك من شأنه أن يوفر رؤى جيدة حول كيفية عمله في الممارسة العملية.

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

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

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

لكن شكرا ، النقطة التي اتخذت.

griesemer أقدر أنه قد يكون مسعى مكلفًا. في هذه الحالة لن أفعل ذلك. إذا كان الأمر يتعلق ببساطة بتطبيق مشروعك المبتذل ، فقد تكون التكلفة محدودة. ولكن هذا أمر يجب أن يحدده موظف Google (وأنا لست كذلك).

أتفهم أنك لن تتمكن من الإبلاغ عن مشاريع Google الفردية أو البنية التحتية الداخلية. ومع ذلك ، ربما يمكن مشاركة بعض الحكايات. ولكن ما أجده شخصيًا أكثر قيمة هو أن بعض موظفي Google يقدمون تقارير حول كيفية نجاحهم. بدون الخوض في التفاصيل ، فإن عبارات مثل "كنت أتوقع X ولكن عندما واجهت حالات مثل A و BI وجدت أن Y" سأجدها ذات قيمة كبيرة. لن أجد أي حاجة للتحقق من هذا النوع من التقارير.

أخيرًا ، قد لا تكون قاعدة رموز Google تمثيلية.

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

احتفظ بها كما هي ، حتى نجد حلاً أفضل ، فإن try ليس هو الحل الذي كنا نبحث عنه. لا داعي لاتخاذ إجراءات متحيزة في هذا الشأن ، خذ وقتك في Go Authors. أعتقد بشدة ، بقدر ما أتحدث مع gophers في جميع أنحاء العالم كل يوم ، أن غالبية Go Community لا تتبنى اقتراح try ATM.

إن تقديم صيغة جديدة يعني أنه يتعين على الجميع ترقية إصدار Go الخاص بهم. ما زلت أستخدم Go 1.10 لأن سير العمل الخاص بي يعتمد على حقيقة أنه يمكنني go get أشياء ثم استخدامها من سطر الأوامر ( GOPATH موجود في PATH ) . لقد واجهت مشكلة مؤخرًا عند محاولة إنشاء مكتبة شخص آخر تستخدم الوحدات النمطية go get . تلقيت خطأ مفاده أن .../v2 لم يكن متاحًا. هذا يعني أن هناك بالفعل انقسام في الكود (فكر في Python 2 و 3). بالنسبة لي ، هناك عالم قبل Go 1.11 وبعد Go 1.11. هذا أمر مزعج للغاية وإدخال بناء جملة جديد لشيء يعمل بالإضافة إلى معالجة الأخطاء في Go ليس مقايضة جيدة على الإطلاق. يقدم المزيد من التجزئة.

في 7/4/19 ، كتب gonutz [email protected] :

إن تقديم صيغة جديدة يعني أن على الجميع ترقية Go
إصدار. ما زلت أستخدم Go 1.10 لأن سير العمل الخاص بي يعتمد على الحقيقة
أنه يمكنني go get للأشياء ثم استخدامها من سطر الأوامر (my
GOPATH موجود في PATH ). لقد واجهت مشكلة مؤخرًا عند محاولة go get
مكتبة شخص آخر تستخدم وحدات. لقد تلقيت خطأ مفاده أن .../v2 كان كذلك
غير متوفر. هذا يعني أن هناك بالفعل انقسام في الكود (فكر
بايثون 2 و 3). بالنسبة لي ، هناك عالم قبل Go 1.11 وبعد Go 1.11.
هذا أمر مزعج للغاية ويقدم صيغة جديدة لشيء يعمل
بالإضافة إلى معالجة الأخطاء في Go ليست مقايضة جيدة على الإطلاق. هو - هي
يقدم المزيد من التجزئة.

إذا كان هناك أي عزاء ، فأنا في نفس الموقف تمامًا
وحدات اذهب. لم أجد الوقت والفرصة لأتعرف
معهم ، لذلك أنا ملتزم بـ Go1.10 أيضًا. ربما يجب أن يكون
مسح يستحق القيام به.

لوسيو.

-
أنت تتلقى هذا لأنه تم ذكرك.
قم بالرد على هذا البريد الإلكتروني مباشرة أو قم بعرضه على GitHub:
https://github.com/golang/go/issues/32825#issuecomment -508372318

-
لوسيو دي ري
2 شارع بيت ريتيف
كيستل (الولاية الشرقية الحرة)
9860 جنوب افريقيا

هاتف: +27 58653 1433
خليوي: +27 83251 5824
فاكس: +27 58653 1435

أنا مطور جديد لـ golang (ما زلت أتعلم المزيد). أعتقد أن معالجة الأخطاء الحالية جيدة لأنها تجعلنا ندير الخطأ بسهولة. بصفتي مطورًا لنظام Android ، أعتقد أن إدارة خطأ try-catch أصعب من إدارة خطأنا من if err != nil{ } في golang. وأعتقد أن معالجة الخطأ الصريح دائمًا أفضل من معالجة الخطأ الضمني.

ملاحظة. آسف لغتي.

leave it alone

لم ينكسر ....

أحب الميم ، @ Daniel1984 :-)

بالمناسبة ، اقتراح try لا يترك if err != nil لوحده ؛ يمنحك فقط خيارًا إضافيًا حيث يكون ذلك منطقيًا.

أنا من رأيي أنه لا ينبغي تضمين try . عند تضمين try :

طليعة

  • المبرمجون يقللون من عدد ضغطات المفاتيح التي يقومون بها.
  • يمكن أن يكون للمبرمجين اختصار للعودة من الوظيفة الحالية على غرار الماكرو.
  • ليس مطلوبًا.
  • سيكون شائع الاستخدام.
  • من الواضح أين يحدث السحر (على عكس Java throws ).
  • لم تعد العيون تلمع عند مشاهدة بحر شيكات nil .
  • يعمل بشكل أفضل مع عمليات التنفيذ البسيطة.

يخدع

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

griesemer هذا بالضبط ما لا أحبه. يجب أن تكون الأمور بسيطة ، لا أريد إضافة تعقيد إلى اللغة لمجرد وجود طريقتين لتحقيق نفس الشيء. توجد أنماط لتجنب الإسهاب هذا if err != nil . https://www.ardanlabs.com/blog/2019/07/an-open-letter-to-the-go-team-about-try.html

يضيف اقتراح Go2 رقم 32437 بناء جملة جديدًا إلى اللغة لجعل لغة النمذجة if err != nil { return ... } أقل تعقيدًا.

هناك العديد من المقترحات البديلة: # 32804 و # 32811 لأن الاقتراح الأصلي غير محبوب عالميًا.

لطرح بديل آخر في المزيج: لماذا لا نحتفظ به كما هو ؟

لقد أحببت الطبيعة الصريحة لمركب if err != nil وبالتالي لا أفهم سبب حاجتنا إلى بناء جملة جديد لهذا الغرض. هل الأمر حقا بذلك السوء؟

كثيرا هذا. الكود الصريح هو الكود الصحيح. إن الرعب الذي رأيته مع المتعاملين الاستثنائيين يكفي للابتعاد إلى الأبد عن هذا البناء الرهيب غير المقروء.

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

هذا يحتاج إلى أخذ في الاعتبار أيضا. من الواضح أن شجرة العنب ليست كذلك
سريعًا كما يظن المرء.

لوسيو.

وتغيير gofmt للسماح بعبارات سطر واحد إذا كانت بسيطة باليد والخطأ تصل إلى
وظيفة الاتصال. هذا من شأنه أن يزيل الكثير من الفوضى.

بدلا من:

يخطئ = myFunction ()
إذا أخطأت! = لا شيء {
العودة يخطئ
}

السماح:

يخطئ = myFunction ()
إذا أخطأ! = لا شيء {إرجاع خطأ}

بالمناسبة ، اقتراح المحاولة لا يترك إذا أخطأ! = لا شيء وحده ؛ يمنحك فقط خيارًا إضافيًا حيث يكون ذلك منطقيًا.

هذا التبرير الدقيق هو كيف يصبح Go آخر C ++ و C # و Scala و Kotlin وما إلى ذلك.

تحرير - قد يكون هذا قد حدث بطريقة خاطئة. أنا لا أقول أن try سيتحول Go إلى لغة مميزة. أنا أقول أن التبرير هنا معيب بعض الشيء

deanveloper لديك مثال واضح على صعوبة فهم سلوك الخطأ باستخدام "try":
https://github.com/golang/go/issues/32437#issuecomment -498932961

حتى لو كان متكررًا قليلاً ، فأنا أتفق مع OP.
بالإضافة إلى ذلك ، تقدم جميع البدائل المقترحة تعقيدًا عديم الفائدة.

سيكون من الرائع حذف الأقواس المتعرجة عندما يكون لديك سطر واحد فقط داخل النطاق

تضمين التغريدة

هذا يعني أن هناك بالفعل انقسام في الكود (فكر في Python 2 و 3). بالنسبة لي ، هناك عالم قبل Go 1.11 وبعد Go 1.11.

أنا مبرمج Python منذ فترة طويلة و "الانقسام" المزعوم الذي ذكرته بخصوص وحدات Go لا يُقارن بكارثة الترحيل من Python 2 إلى Python 3.

هذا أمر مزعج للغاية وإدخال بناء جملة جديد لشيء يعمل بالإضافة إلى معالجة الأخطاء في Go ليس مقايضة جيدة على الإطلاق. يقدم المزيد من التجزئة.

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

تضمين التغريدة

أعتقد أن معالجة الأخطاء الحالية جيدة لأنها تجعلنا ندير الخطأ بسهولة. بصفتي مطورًا لنظام Android ، أعتقد أن إدارة خطأ try-catch أصعب مما لو كان Error! = nil {} في golang. وأعتقد أن معالجة الخطأ الصريح دائمًا أفضل من معالجة الخطأ الضمني.

هل قرأت الاقتراح بالكامل؟ try هو مجرد وظيفة مضمنة تساعد في استبعاد تكرار if err != nil { return ..., err } . يبقى المنطق العام لمعالجة الأخطاء في Go كما هو. لا يزال واضحًا ، ولا تزال الأخطاء عبارة عن قيم ، ولا يوجد تجربة (تُعرف أيضًا باسم الاستثناءات).

تضمين التغريدة

  • يضيف try طريقة مكررة لعملية حالية.

try يحسب الكود التكراري. إنه نفس الشيء مع append . يمكننا كتابتها بأنفسنا في كل مرة نقوم فيها بإلحاق عناصر بشريحة ، ولكن من الأسهل استدعاء append .

  • يضيف تناقضات للتحقق من الأخطاء.

يمكنك معالجة شريحة "يدويًا" باستخدام [...:...] أو يمكنك استخدام append ، اعتمادًا على ما تفعله. لا يوجد تناقض. إنها مجرد أدوات مختلفة لمهام مختلفة. نفس الشيء بالنسبة للأخطاء ، مع if err != nil { ... } عادي ، أو بـ try ، اعتمادًا على المهمة المطروحة.

  • بالنسبة إلى try للعودة من الوظيفة الحالية غير متوقع ، فإن AKA أكثر سحرًا.

إنه غير متوقع لأنه جديد. لقد اعتدنا عليها كلما استخدمناها أكثر. ولا أعتقد أنه سحر. مواصفات المحاولة واضحة جدًا.

  • لن يفهم المبرمجون الذين ليس لديهم خبرة Go.
  • من الصعب حتى وصف ما يحدث عند try بالكلمات.

المبرمجون الذين ليس لديهم خبرة في Go لن يفهموا chan ، defer ، 'go , iota , panic , استرداد , <- , type assertions, and many other things either without reading the documentation. try` سهل مقارنة بمعظم هذا.

  • لا يغير معالجة الخطأ.

ربما يكون هذا أمرًا جيدًا ، وفقًا لما قاله gophers طلب ترك if err != nil بمفرده ؛-)

تضمين التغريدة

الكود الصريح هو الكود الصحيح. إن الرعب الذي رأيته مع المتعاملين الاستثنائيين يكفي للابتعاد إلى الأبد عن هذا البناء الرهيب غير المقروء.

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

تضمين التغريدة

سيكون من الرائع حذف الأقواس المتعرجة عندما يكون لديك سطر واحد فقط داخل النطاق

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

تضمين التغريدة
الاعتدال وإبقاء اللغة بسيطة أمر مهم.

قد تبرر بعض الحجج التي استخدمتها قدرًا كبيرًا من التغيير في المواصفات. لا توجد نقاط إيجابية فقط هنا.

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

مرحبًا kroppt ،

من المهم الحفاظ على اللغة بسيطة

أوافق وأعتقد أننا جميعًا نسعى جاهدين من أجل ذلك.

أقوم بتقييم القرار بناءً على ما إذا كان سيساعد أو يضر ، وليس بالضرورة سن فلسفة معينة بشكل كامل.

أعتقد أننا جميعًا نقوم بتقييم try بناءً على فوائده وتكاليفه. تدور المناقشة حول تحديد وإيجاد إجماع قائم على الحقائق حول تلك الفوائد والتكاليف ، وهو ما حاولت القيام به في تعليقي السابق.

أنت محق في أن بعض الأشياء في المواصفات تنتهك بعض المبادئ التي تأسست عليها

خلال السنوات القليلة الماضية ، كنت أقرأ كل ما نشره فريق Go تقريبًا حول Go وتصميمه ، ولا أفهم In ما تقصده. لا أعتقد أن اقتراح try ينتهك المبادئ الأساسية لـ Go.

تضمين التغريدة
https://talks.golang.org/2012/splash.article يصف بعض المفاهيم الكامنة وراء ما يجعل go مختلفًا - الوضوح والبساطة ، من بين أمور أخرى. أعتقد أن هذا هو الصراع الذي يراه البعض منا مع هذا التغيير الجديد. إنه أبسط ، لكنه أقل وضوحًا. بالنسبة لي ، يبدو أن المكسب في البساطة أقل من فقدان الوضوح. ربما أكون مخطئا وأنا فقط حذر.

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

تضمين التغريدة
ما تصفه موجود في قسم "المحترفين" الخاص بي:

  • المبرمجون يقللون من عدد ضغطات المفاتيح التي يقومون بها.
  • يمكن أن يكون للمبرمجين اختصار للعودة من الوظيفة الحالية على غرار الماكرو.
  • لم تعد العيون تلمع عند مشاهدة بحر شيكات nil .

مرة أخرى ، لا أرى فائدة من ذكر التطبيق الشامل لمبدأ ما عندما لا نطبقه هنا عالميًا.

أنا لا أتفق مع فكرة أن المحاولة تقوم بتشويش الشفرة

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

أعتقد أن go حقق في الأصل توازنًا رائعًا في البساطة ، لدرجة أنه ساهم في الوضوح بدلاً من الابتعاد عنه. لا أستطيع أن أشرح كيف فعلوا ذلك ، لكن try في رأيي لا يفعل ذلك.

لا أعتقد أننا يجب أن نعتبر الإسهاب مشكلة. يجب قراءة التعليمات البرمجية وفهمها من قبل البشر - الذين يكون وقتهم أكثر تكلفة من وقت أجهزة الكمبيوتر - ويميل "الفهم" إلى أن يكون الجزء الصعب ويستغرق وقتًا طويلاً.

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

أعتقد أيضًا أنه على الرغم من أن الشيكات if err != nil قد تبدو مملة ، إلا أنني لست بحاجة إلى نوعها. أنا فقط لدي محرر بلدي يفعل ذلك.

تضمين التغريدة

الهدف من التغيير هو تعتيم / إخفاء / تبسيط / تمثيل الكود - وإلا فإننا سنرى كتلة التحقق من الخطأ الأصلية.

لكن يمكنك استخدام هذه الوسيطة لأي استدعاء وظيفة! إذا اتصلت بـ strings.HasPrefix("foobar", "foo") ، فهل يؤدي ذلك إلى تشويش الرمز؟ هل تفضل كتابة وقراءة l := len("foo"); len("foobar") >= l && s[0:l] == "foo" ؟

تضمين التغريدة

كل فحص خطأ صريح.

try لا يزال التحقق صراحة من الخطأ. إنه سبب وجود المحاولة.

أعتقد أيضًا أنه إذا أخطأت!

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

try لا يزال التحقق صراحة من الخطأ
الفرق بين التجريد والسلاسل try HasPrefix هو أن try يعود ضمنيًا.
عند قراءة كود go ، أعلم أن تدفق التنفيذ يظل ضمن وظيفتي حتى:

  • اقرأ قوس الإغلاق لوظيفة بدون أنواع إرجاع
  • قراءة الكلمات الرئيسية return ، panic
  • قراءة syscall.Exit(code)
    مع try ، لم أتمكن من قراءة الكود بنفس الطريقة. إن المسح البصري للخطوط ورؤية عبارات "العودة" الصفرية لم يعد يعني "إما أن هذه الخطوط كلها تنفذ ، أو كتلة واحدة ، أو أن البرنامج ينتهي."

ngrilly يمكنك الرد على أكثر من شخص في if err != nil . يأتي هذا على حساب تقديم طرق جديدة لتسريب الموارد ، والقدرة على كتابة كود أقل إيجازًا وأسوأ من ذلك كله يسمح بتداخل المحاولة .

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

هناك تكلفة كبيرة هنا لتبريرها بقيمة واحدة مضافة أراها مرتجعة في ردود المؤيدين: "لم أعد مضطرًا إلى كتابة if err != nil " - الشيء الذي تم الدفاع عنه بشدة وترسيخه مع الأخطاء القيم من قبل مجتمع Go بأكمله. لقد اقتربنا من عقد من الكود المكتوب باستخدام if err != nil - والذي تستخدمه جميعًا من أبرز التطورات التكنولوجية (docker، k8s، ...) في نفس الوقت بنجاح كبير.

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

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

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

يجب أن يكون شريط إضافة شيء كهذا أعلى من ذلك بكثير ، ويجب أن يكون تجريبيًا لفترة طويلة لإثبات أنه أفضل مع البيانات من الحقل.

تضمين التغريدة

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

اقترح إيان

تضمين التغريدة

الفرق بين محاولة التجريد والسلاسل. HasPrefix هو أن المحاولة تعود ضمنيًا.

هذا صحيح. عند قراءة دالة والبحث عن نقاط "خروج" ، علينا البحث عن العودة والذعر والسجل والذعر و os.Exit و log.Fatal وما إلى ذلك ... ومحاولة. هل هذه مشكلة؟ سيبقى عدد نقاط الخروج في الوظيفة كما هو وسيظل محددًا بشكل صريح ، مع المحاولة أو بدونها.

قراءة عودة الكلمات الرئيسية والذعر

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

عند قراءة دالة والبحث عن نقاط "خروج" ، علينا البحث عن العودة والذعر والسجل والذعر و os.Exit و log.Fatal وما إلى ذلك ... ومحاولة. هل هذه مشكلة؟

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

تضمين التغريدة

اقترح إيان منذ 7 أيام نقل هذه المناقشة إلى golang-nuts لهذا السبب بالضبط (لا توجد طريقة للرد على تعليق محدد ، بدون خيط) ، وهو الاقتراح الذي تم رفضه للتأكد من أن المناقشة ستكون "رسمية". لدينا ما طلبناه.

بدء الرسالة

@ user1
الرد 1

@ user2
الرد 2

رسالة النهاية

هذا هو المقصود.

تضمين التغريدة

لقد رأيت فائدة واحدة: منع كتابة إذا أخطأت! = لا شيء.

try يمنع الكتابة المتكررة وقراءة من if err != nil { return ..., err } (منسق على 3 خطوط)، وليس فقط if err != nil .

يأتي هذا على حساب تقديم طرق جديدة لتسريب الموارد ، والقدرة على كتابة كود أقل إيجازًا وأسوأ من ذلك كله يسمح بتداخل المحاولة.

يمكن منع مخاطر تسرب الموارد التي ذكرتها باستخدام vet و lint .

حول "رمز أقل إيجازًا" ، الهدف من try هو كتابة كود أكثر إيجازًا ، لذلك أنا لا أفهم حجتك.

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

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

ربما هذا الشعور متبادل. نحن جميعًا محبوبون رائعون. دعونا لا نقع في حكم القيمة ؛-)

أرى ارتجاعًا في ردود المؤيدين: "لم أعد مضطرًا للكتابة إذا أخطأت! = لا شيء"

مرة أخرى ، لم أعد مضطرًا إلى كتابة l := len("foo"); len("foobar") >= l && s[0:l] == "foo" .
يمكنني استخدام strings.HasPrefix("foobar", "foo") بدلاً من ذلك.
كيف تختلف مع try ؟
لقد قرأت سابقًا أنك ستقبل "مقيدًا" try والذي سيتم تسميته check وسيحظر التداخل.

لقد اقتربنا من عقد من الكود المكتوب باستخدام if Error!

لدينا أيضًا الكثير من الشفرات الرائعة المكتوبة بلغة C و C ++ و Java وما إلى ذلك. مع هذا الخط من التفكير ، لن يكون لدينا Go.

عند قراءة المناقشات حول معالجة الأخطاء في Go ، لم أشعر أن الجميع كانوا على نفس الصفحة فيما يتعلق بالاقتراح try ، لذلك قررت كتابة منشور مدونة يوضح كيف يمكن لـ try يمكن استخدامها: https://faiface.github.io/post/how-to-use-try/

المناقشة ذات الصلة على Reddit: https://www.reddit.com/r/golang/comments/c9eo3g/how_to_use_try_faiface_blog/

أعلم أن هذه المشكلة ضد try ، لكنني آمل أن تجلب منشوري بعض وجهات النظر الجديدة :)

غو عالق ويكافح بين سحري أو منطقي لفكرة بسيطة.

الايجابيات:

  • تقليل المتداول
  • بسيط
  • النمط الحالي بين اللغات الأخرى
  • اختياري

سلبيات:

  • منحنى التعلم
  • السحر الأساسي
  • أنواع جديدة من البق
  • Go هو رأي ، وواقعي مع if != nil ولكن يمكنك استخدام try

أشعر أن هذا المجتمع esp. هنا يختلف ضد الأشخاص الذين صوتوا في Go Survey [1] .
قد لا يختار الناخبون هذا باعتباره مصدر قلق رئيسي بدلاً من تركه لاعتبارات مستقبلية.
لكن كان يُنظر إلى أن لها تأثيرًا بسبب موضعها.

IMO ، ميزة إضافة اللغة قديمة وتضيف طريقة البرمجة الحديثة المزيد من الميزات إلى المحررين ، مثل Emmet أو مقتطفات اللغة ، وطي الكود وتلوينه ، وإعادة البناء والتهيئة ، وتصحيح الأخطاء والاختبار ، واقتراح حل لخطأ ما والاستشهاد من godoc أو مكدس الفائض ، وواجهة المستخدم أعلى شفرة المصدر ، واترك شفرة المصدر مطولة
طي الكود if err != nil في try

تضمين التغريدة

حاول منع الكتابة المتكررة وقراءة إذا أخطأت!

هل تصدقني أن قول "يمنعك هذا من الكتابة إذا كنت تخطئ! = لا شيء" يعني أنني نسيت تمامًا أننا قرأنا أيضًا الكود الذي نكتبه؟

يمكن منع مخاطر تسرب الموارد التي ذكرتها بالطبيب البيطري والوبر.

لقد ربطت مناقشة لماذا أشعر بأنني طبيب بيطري والوبر ليس خيارًا معقولًا هنا.

حول "الشفرة الأقل إيجازًا" ، فإن بيت القصيد من المحاولة هو كتابة كود أكثر إيجازًا ، لذلك أنا لا أفهم حجتك.

نعم ، هل قرأت الرابط الذي أشارت إليه "القدرة على كتابة رمز أقل إيجازًا " في الواقع ، فربما تكون قد فهمت حجتي. _ لاحظ أن تخصيص وقت قصير لفهم حجج الأشخاص الذين تشارك في المناقشة معهم هو الخطوة الأولى لتقديم معلومات قادرة على جعلهم يتنازلون عن وجهة نظرك ._

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

مثير للاهتمام ، دعنا نقسم هذا:

1) خطر تداخل استدعاء الوظيفة المفرط ليس محددًا للمحاولة.

نعم ، الجميع هنا يفهم كيف تعمل الوظائف.

2) يمكن أن تكون أي استدعاءات وظيفية متداخلة بشكل مفرط.

نعم ، الجميع هنا يفهم كيف تعمل الوظائف.

3) ستساعد مراجعات الكود والفحص ، كما هو الحال دائمًا.

نعم ، لقد قدمت بالفعل وسيطة lint و "وسيطة مراجعات الكود" هي عنصر تحكم آخر في اللغة الخارجية تم إجراؤه في المشاركات التي ربطتها بك .

ربما هذا الشعور متبادل. نحن جميعًا محبوبون رائعون. دعونا لا نقع في حكم القيمة ؛-)

أنا لا أفهم؟ لا يحتوي الاقتراح على أمثلة على القدرة الكاملة التي يوفرها التنفيذ ، فقط الاستخدام المقصود. أداة tryhard المستخدمة للمساعدة في قياس تأثير الاقتراح تستخدم try في الشكل الأكثر محدودية ومعقولة ، هذه حقيقة بسيطة.

مرة أخرى ، لم أعد مضطرًا إلى كتابة l: = len ("foo")؛ len ("foobar")> = l && s [0: l] == "foo".
يمكنني استخدام سلاسل. HasPrefix ("foobar"، "foo") بدلاً من ذلك.
كيف هو مختلف جدا مع المحاولة؟

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

كيف تختلف ( strings.HasPrefix ) مع try ؟

السلاسل

func hasPrefix

func HasPrefix (s ، سلسلة بادئة) منطقي

تختبر HasPrefix ما إذا كانت السلسلة s تبدأ بالبادئة.

محاولة

func try هي وظيفة مضمنة جديدة تشبه الوظيفة تسمى try with signature (pseudo-code)

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

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

يمكن استخدام المحاولة المضمنة فقط داخل دالة ذات معامل نتيجة واحد على الأقل حيث تكون النتيجة الأخيرة من نوع الخطأ. يؤدي استدعاء المحاولة في سياق مختلف إلى حدوث خطأ في وقت الترجمة.

استدعاء المحاولة باستدعاء الوظيفة f () كما في (pseudo-code)

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

turns into the following (in-lined) code:

t1, … tn, te := f()  // t1, … tn, te are local (invisible) temporaries
if te != nil {
        err = te     // assign te to the error result parameter
        return       // return from enclosing function
}
x1, … xn = t1, … tn  // assignment only if there was no error

بعبارة أخرى ، إذا كانت القيمة الأخيرة الناتجة عن "expr" ، من نوع الخطأ ، هي صفر ، فحاول ببساطة إرجاع قيم n الأولى ، مع إزالة الخطأ النهائي nil. إذا كانت القيمة الأخيرة الناتجة عن "expr" ليست صفرية ، يتم تعيين متغير نتيجة خطأ وظيفة التضمين (يسمى خطأ في الرمز الزائف أعلاه ، ولكن قد يكون له أي اسم آخر أو يكون غير مسمى) على قيمة الخطأ non-nil و إرجاع وظيفة التضمين. إذا كانت وظيفة التضمين تعلن عن معلمات نتائج مسماة أخرى ، فإن معلمات النتيجة هذه تحتفظ بأي قيمة لديها. إذا كانت الوظيفة تعلن عن معلمات نتائج أخرى غير مسماة ، فإنها تفترض قيم الصفر المقابلة لها (وهو نفس الاحتفاظ بالقيمة التي لديها بالفعل).

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

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

ستعمل دائمًا على تعيين "أ" و "ب" وخطأ ، بصرف النظر عما إذا كانت f () قد أعادت خطأ أم لا. فى المقابل

a, b = try(f())

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

إنها مختلفة من حيث أن النص الكامل الموجود في وصف المحاولة غير موجود في السلاسل. سيكون السؤال الأفضل هو كيف يكون التشابه ، والذي أود أن أجيب عليه كلاهما يشتركان في بعض جوانب المكالمات ولا شيء آخر.

قرأت سابقًا أنك ستقبل محاولة "مقيدة" من شأنها أن تسمى شيكًا وتمنع التداخل.

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

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

fn := func(n int) (int, error) { ... }
return try(func() (int, error) { 
    mu.Lock()
    defer mu.Unlock()
    return try(try(fn(111111)) + try(fn(101010)) + try(func() (int, error) {
       // yea...
    })(2))
}(try(fn(1)))

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

يوفر Go أقل الطرق الممكنة للمطورين الآخرين وأنا لإضاعة وقت بعضنا البعض من خلال قصرنا على استخدام نفس التركيبات الدنيوية. لا أريد أن أفقد ذلك بدون فائدة كبيرة. لا أعتقد أن عبارة "لأن المحاولة يتم تنفيذها كوظيفة" ستكون ذات فائدة كبيرة. هل يمكنك تقديم سبب لسبب ذلك؟

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

غو عالق ويكافح بين سحري أو منطقي لفكرة بسيطة.

الايجابيات:

  • تقليل المتداول
  • بسيط
  • النمط الحالي بين اللغات الأخرى
  • اختياري

سلبيات:

  • منحنى التعلم
  • السحر الأساسي
  • أنواع جديدة من البق
  • Go هو رأي ، وواقعي مع if != nil ولكن يمكنك استخدام try

أشعر أن هذا المجتمع esp. هنا يختلف ضد الأشخاص الذين صوتوا في Go Survey [1] .
قد لا يختار الناخبون هذا باعتباره مصدر قلق رئيسي بدلاً من تركه لاعتبارات مستقبلية.
لكن كان يُنظر إلى أن لها تأثيرًا بسبب موضعها.

IMO ، ميزة إضافة اللغة قديمة وتضيف طريقة البرمجة الحديثة المزيد من الميزات إلى المحررين ، مثل Emmet أو مقتطفات اللغة ، وطي الكود وتلوينه ، وإعادة البناء والتهيئة ، وتصحيح الأخطاء والاختبار ، واقتراح حل لخطأ ما والاستشهاد من godoc أو مكدس الفائض ، وواجهة المستخدم أعلى شفرة المصدر ، واترك شفرة المصدر مطولة
طي الكود if err != nil في try

كنت أحد الذين صوتوا للتعامل مع الأخطاء بشكل أكثر صرامة دون إمكانية نسيان معالجة الخطأ. ليس للمحاولة.

سنحتاج إلى ما هو أكثر بكثير من الأدوية الجنيسة لإعادة صنع أي شيء عن بعد مثل نوع Rust Result . حتى _if_ يمكن صنع النوع Result فقط باستخدام الأدوية الجنيسة ، سيحتاج المبرمجون المبتدئون بعد ذلك إلى معرفة الأدوية الجنسية قبل أن يتمكنوا حتى من معالجة الخطأ بشكل صحيح ، أو إرجاع خطأ من دالة "طريقة Result "

deanveloper ، وجهة نظري هي: لدي الكثير للاستفادة من الأدوية الجنيسة أكثر من "تغيير بناء الجملة" وأعتقد أنه ينطبق أيضًا على المجتمع.

txgruppi يمكنني أن أوافق على أن الأدوية الجنيسة يجب أن يكون لها أولوية أعلى. كنت أحاول فقط أن أقول إنني لا أعتقد أن الأدوية الجنيسة ستكون بديلاً جيدًا لمعالجة الأخطاء.

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

هل يمكن حلها باستخدام مقتطف أو ماكرو لوحة المفاتيح؟ ثم انها ليست قضية.

تضمين التغريدة

فقط تعلم كيفية كتابة كود أفضل وحل هذه المشكلة بتغييرات التصميم.

70٪ من أكواد معالجة الأخطاء في المكتبة القياسية مناسبة حاليًا لـ try كما اكتشف روبرت جريسيمر fmt.HandleErrorf وظيفة. آمل ألا ترغب في تسمية المكتبة القياسية بشفرة سيئة.

هل يمكن حلها باستخدام مقتطف أو ماكرو لوحة المفاتيح؟ ثم انها ليست قضية.

يتعلق الأمر أيضًا بقراءة الكود. لهذا السبب لا نحب thing.Thing thing = new thing.Thing(thing.THING);

faiface ، هل 'إذا أخطأت! = لا شيء' في طريقها لتطوير برامج عالية الجودة؟ لا أعتقد ذلك.
هل الافتقار إلى Generics يعرقل طريق تطوير برامج عالية الجودة؟ نعم إنه كذلك.

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

faiface ، المكتبة القياسية ليست تمثيلًا جيدًا لرمز Go الحقيقي. هذا لأنه من المرجح أن تقوم المكتبة القياسية ببساطة بإلغاء الأخطاء دون إضافة سياق ، على سبيل المثال io/ioutil لا تحتاج أبدًا إلى تزيين الأخطاء ، يمكنها ببساطة تفويت الخطأ الذي حدث في io . اعترف Robert Griesemer أيضًا بأن stdlib ليس بالضبط أفضل ممثل لكود Go الواقعي ، ولكن أنا على الهاتف المحمول الآن ولا أريد البحث عن التعليق. أنا متأكد من أنه كان قريبًا نسبيًا من موقعه الأصلي في محاولة.

deanveloperfaiface عندما تشغيل ضد العودة كوربوس :

--- stats ---
 401679 (100.0% of  401679) func declarations
  97496 ( 24.3% of  401679) func declarations returning an error
 991348 (100.0% of  991348) statements
 217490 ( 21.9% of  991348) if statements
  88891 ( 40.9% of  217490) if <err> != nil statements
    485 (  0.5% of   88891) <err> name is different from "err" (-l flag lists details)
  59500 ( 66.9% of   88891) return ..., <err> blocks in if <err> != nil statements
  29391 ( 33.1% of   88891) complex error handler in if <err> != nil statements; cannot use try (-l flag lists details)
    596 (  0.7% of   88891) non-empty else blocks in if <err> != nil statements; cannot use try (-l flag lists details)
  52810 ( 59.4% of   88891) try candidates (-l flag lists details)

لذلك ، في الكود الواقعي ، 40٪ من كشوفات if- مكتوبة للتحقق من الأخطاء ، ويمكن أن يزيل try 59٪ منها خارج الصندوق.

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

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

في 7/5/19 ، كتب Nicolas Grilly [email protected] :

kroppt لقد قرأت عشرات المرات ؛-) لا أتفق مع فكرة المحاولة
التعتيم على الشفرة. try هي مجرد وظيفة مضمنة تستخدم لتحليل بعض العوامل
كود متكرر. نحن نفعل ذلك طوال الوقت كمبرمجين. عندما نحدد ملف
النمط المتكرر ، نحللها في دالة جديدة. إذا لم نفعل ، نحن
سيكون لها وظيفة رئيسية طويلة واحدة مع كل التعليمات البرمجية الخاصة بنا مضمنة فيها.

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

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

لا تهتم بالأطواق المشتعلة التي يقدمها استخدام "التأجيل" للتعامل
مع المزيد من الاستخدامات الغامضة لـ "محاولة - الوظيفة الزائفة". شخص ما
قال في مكان آخر تقريبًا ، "لا أريد أن أجد try في الكود
قرأت ". أشعر بنفس الشيء ولا ينبغي أن أجرف تحت
سجادة.

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

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

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

لتوضيح هذه النقطة ، ضع في اعتبارك قيم الإرجاع من القارئ:
io.EOF هي حالة خاصة تكون أحيانًا ناجحة وأحيانًا ملف
الفشل ، ولكن وفقًا لمعايير Go ، يعد خطأً بكل فخر ("io.Err! = nil").
هل سيكون لدينا طريقة ما لاختصار ذلك أيضًا؟ بكل تأكيد
لا ، لأننا معتادون على "مسامحة" خطأها.

لقد انتظرت منذ فترة طويلة خروج حلقة لنقل "حالة" مماثلة أو
كود الشرط (قد ينتهي البحث بقيمة تم العثور عليها أو بفشل ، كيف
هل تخبر أيها إذا كنت ترغب في القيام بأشياء مختلفة؟ قمت بإضافة اختبار ،
حيث المعرفة موجودة بالفعل - نفس المشكلة ، مختلفة
سياق الكلام).

هذه تحسينات حقيقية للغات التقليدية: تقليل الغلاية
لوحة ، بالمقارنة ، سخيفة.

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

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

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

بناء "تأجيل" ضمن معالجة الأخطاء (الذي تبنته -
دون أن تدرك استيرادها في مكان آخر - باستمرار في وضع اللمسات الأخيرة
معاملات SQL: كان tx.Rollback / tx.Commit) بمثابة الوحي بالنسبة لي.
قد يكون هناك المزيد من المبادئ التي يمكن تعلمها "ضمن" نطاق
ما تقدمه Go بالفعل: دعنا نبقى داخل هذا المربع في الوقت الحالي.

أحد هذه الأشياء ، بعيدًا عن الكفة ، هو تمرير الإبلاغ عن الخطأ
وظيفة قائمة "error.Methods" ليتم تنفيذها بموجب الاصطلاحية
الشروط (io.EOF ، sql.ErrNoRows) ، بدلاً من الإبلاغ عن النتيجة
كالأسود والأبيض. لكنني لم أتعلم في مثل هذه الأمور ، اقتراحاتي
ساذجون للغاية ، دع الآخرين (روجر ، هل تستمع؟) يتغذون بطريقة مماثلة
أفكار تؤتي ثمارها.

لوسيو.

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

مرة أخرى ، مخادع أو على الأقل عقلاني. أنا أوافق على أن هؤلاء
تتم قراءة ثلاثة أسطر مرات أكثر مما هو مكتوب ، ولكن
ألم جريسيمر يهدف إلى تخفيفه في الكتابة وليس في القراءة

  • وهو ما يراه بلا شك نتيجة مفيدة.

لكن "err! = nil" علامة مألوفة للغاية وعندما "تقرأ" - مثل
على عكس "البحث" باستخدام محرر ، فإن هذا النمط سهل في نفس الوقت
بقعة وسهلة الإلغاء. العوملة ليست في نفس الشيء
الدوري على الإطلاق. والسعر خاطئ.

"نحن كمبرمجين" نميل إلى تحديد أنماط أكثر تعقيدًا أولاً ،
حتى لو حدثت بشكل نادر. ونعلم أيضًا أنه "إذا لم يخطئ! = لا شيء {إرجاع
err} "في سلسلة بسيطة جدًا من التعليمات ، إذا كان
لا أحد ، دعهم يرفعوا أيديهم هنا. يمكن للمرء أن يكون على قدم المساواة
واثق من أنه سيحدث مع "محاولة - الوظيفة"؟

لوسيو.

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

نحن نأخذ في الحسبان الأنماط المتكررة كمبرمجين ، هذا صحيح تمامًا.

الكثير من التعليمات البرمجية المتكررة تبطئ القراءة ، لذلك لا أرى كيف أن هذا مخادع أيضًا.

حججك المضادة هي في الأساس "هيا يا رجل ، إنها ليست مشكلة كبيرة". حسنًا ، بالنسبة لكثير من الناس هو كذلك.

من يريد أن ينضم الأشخاص الخطأ إلى مجتمعنا؟

هذه بوابة متغطرسة للغاية. أيضًا ، كشفت الأداة tryhard أن try قابل للتطبيق بشكل مباشر في الكثير من قواعد كود Go اليوم. يمكن تطبيقه مباشرة على 70٪ من كود معالجة الأخطاء في المكتبة القياسية. مع التغييرات التي تم إجراؤها على الكود (لاستخدام زخرفة الخطأ باستخدام defer ، إلخ) ، أعتقد أنه سيكون قابلاً للتطبيق على أكثر من 80٪ من معالجة الأخطاء في جميع رموز Go.

متفق عليه ، لقد تجاوزت العلامة هنا. أعتذر.

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

في 7/7/19 ، كتب Michal Štrba [email protected] :

@ lootch يا رجل ، من غير المجدي حقًا وصف الناس بالمخادعين ، أنا
متأكد تمامًا من أنها ليست مثل العبارات التي ميزتها على هذا النحو جميلة
مسؤول.

نحن نأخذ في الحسبان الأنماط المتكررة كمبرمجين ، هذا بالتأكيد
حقيقية.

تؤدي الكثير من التعليمات البرمجية المتكررة إلى إبطاء القراءة ، لذلك لا أرى كيف يحدث ذلك
مخادع سواء.

حججك المضادة هي في الأساس "هيا يا رجل ، إنها ليست مثل
صفقة كبيرة ". حسنًا ، بالنسبة لكثير من الناس.

من يريد أن ينضم الأشخاص الخطأ إلى مجتمعنا؟

هذه بوابة متغطرسة للغاية. أيضًا ، كشفت الأداة tryhard ذلك
try قابل للتطبيق مباشرة في الكثير من قواعد أكواد Go الحالية. يمكن أن يكون
يتم تطبيقه مباشرة على 70٪ من كود معالجة الأخطاء في المكتبة القياسية. مع
التغييرات في الكود (لاستخدام زخرفة الخطأ باستخدام defer ، إلخ) ، على ما أعتقد
سيكون قابلاً للتطبيق على أكثر من 80٪ من معالجة الأخطاء في جميع رموز Go.

-
أنت تتلقى هذا لأنه تم ذكرك.
قم بالرد على هذا البريد الإلكتروني مباشرة أو قم بعرضه على GitHub:
https://github.com/golang/go/issues/32825#issuecomment -508971768

-
لوسيو دي ري
2 شارع بيت ريتيف
كيستل (الولاية الشرقية الحرة)
9860 جنوب افريقيا

هاتف: +27 58653 1433
خليوي: +27 83251 5824
فاكس: +27 58653 1435

lootch الدعائم على أن تكون واعيا! أستطيع أن أفهم الإحباط من رؤية المناقشة في دوائر.

أرى الأمر أيضًا بنفس الطريقة وأنا على الجانب الآخر.

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

في 7/7/19 ، كتب Michal Štrba [email protected] :

[...]
ربما فشل الجانبان في فهم الجانب الآخر. هل
اقرأ منشور مدونتي بعنوان كيفية الاستخدام 'محاولة'؟ حيث أحاول
أظهر كيف سيبدو استخدام "try" عمليًا ، أبذل قصارى جهدي للبقاء
غير متحيزة؟

أعترف أنني لم أفعل ، أتمنى بشدة ألا أضطر أبدًا إلى :-)

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

إذا لم يكن الأمر كذلك ، فالرجاء حثني ، فالمسألة الجيدة للقراءة لها مكانة خاصة
في حياتي :-)

لوسيو.

@ lootch لقد بذلت قصارى جهدي لإظهار أكبر عدد ممكن من جوانب "المحاولة" (على سبيل المثال عندما يتم تطبيقها وعندما لا يتم ذلك) بكمية قصيرة من التعليمات البرمجية وجعلها غير متحيزة وواقعية قدر الإمكان. لكن بالطبع ، لا تأخذ كلامي على محمل الجد :)

كان هذا هو التعليق الأعلى الذي تم التصويت عليه في مناقشة Reddit ذات الصلة :

هذا مثال افتراضي مفيد وغير متحيز. نشكرك على إضافة شيء بنّاء للمحادثة ليس فقط "إنها مقرفة".

@ lootch لقد بذلت قصارى جهدي لإظهار أكبر عدد ممكن من جوانب "المحاولة" (على سبيل المثال عندما يتم تطبيقها وعندما لا يتم ذلك) بكمية قصيرة من التعليمات البرمجية وجعلها غير متحيزة وواقعية قدر الإمكان. لكن بالطبع ، لا تأخذ كلامي على محمل الجد :)

كان هذا هو التعليق الأعلى الذي تم التصويت عليه في مناقشة Reddit ذات الصلة :

هذا مثال افتراضي مفيد وغير متحيز. نشكرك على إضافة شيء بنّاء للمحادثة ليس فقط "إنها مقرفة".

وظيفة مع مسار الملف كوسيطة؟ سيكون هذا وحده سببًا لعدم اجتياز هذا الرمز لمراجعي. ماذا لو كانت بعض الحقول مفقودة؟ أعيد ترتيبها؟

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

لا تؤثر التغييرات المطلوبة لإصلاح المشكلات التي أثارتها على ممارسات معالجة الأخطاء

لأنك قلت ذلك؟

  1. ابدأ من عنوان مدونتك: يجب أن تسمى "كيف لا تكتب" ، لأنني أكرر ، استخدام مسار الملف كمعامل هو ممارسة سيئة حقًا ، وبصراحة ، رمز كامل أدناه أيضًا.
  2. هل تدرك هذا
    go resp := Respondent{ Name: name, Gender: try(parseField(s, &line, "gender")), OS: try(parseField(s, &line, "os")), Lang: try(parseField(s, &line, "lang")), }
    سينتج رسائل خطأ رديئة؟ يجب أن تكون هناك رسالة خطأ حقل غير متوقعة ورسالة خطأ حقول مفقودة على الأقل. تشخيص البرنامج النصي الخاص بك دون المستوى المطلوب.

ألقى PS نظرة على المستودعات الخاصة بك. هل تدرك أن Go أداة سيئة لمهامك؟ يجب أن تفهم في ممارسة تطبيق Real Go أن أول من سيرى السجلات سيكون مهندسي التشغيل وليس المطورين. قد تساعدهم رسالة الخطأ المناسبة في حل المشكلة بأنفسهم.

sirkon تعال ، لا تفعل فلاموارز.

هل تدرك أن هذا سينتج عنه رسائل خطأ سيئة؟

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

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

ألقى PS نظرة على المستودعات الخاصة بك. هل تدرك أن Go أداة سيئة لمهامك؟

هل تقترحون آخر لمهامي؟ لقد استخدمت القليل. راجع للشغل ، هذا خارج الموضوع تمامًا.

تضمين التغريدة

هل تقترحون آخر لمهامي؟ لقد استخدمت القليل. راجع للشغل ، هذا خارج الموضوع تمامًا.

الصدأ؟ C ++؟

تضمين التغريدة

الصدأ؟ C ++؟

هناك نذهب. لقد استخدمت كلاهما قبل الاستقرار مع Go. لم أنظر إلى الوراء أبدًا.

sirkon أحد العيوب الهائلة في try هو أنه لا يشجع على زخرفة الأخطاء. كان المبرمج في هذه الحالة يعرض تطبيقات محتملة try ، لذلك بالطبع لن يكون هناك الكثير من زخرفة الأخطاء.

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

deanveloper شكرا على التعليق!

بالمناسبة

كان المبرمج في هذه الحالة يعرض التطبيقات المحتملة للتجربة ، لذلك بالطبع لن يكون هناك الكثير من زخرفة الأخطاء.

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

parse respondnts.txt: open respondnts.txt: no such file or directory
parse respondents.txt: line 12: parse field gender: expected "gender:"
parse respondents.txt: line 9: expected empty line
parse respondents.txt: line 4: parse field lang: EOF

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

لم يكن الفحص / المقبض ليساعد كثيرًا في حالتك الخاصة. لكن الفكرة المقترحة لـ catch والاقتراحات المضادة الأخرى لـ try كانت ستتمكن من معالجة الأخطاء كإضافة زخرفة إضافية.

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

..., err := functionThatCanFail(...)
try(errors.Wrapf(err, ...))

أو ببساطة قسّم الوظيفة الكبيرة إلى عدة وظائف صغيرة.

faiface في عيني في تلك المرحلة ، يجب على المرء فقط استخدام if err != nil ، لكن أعتقد أنها مسألة تفضيل.

لكن في بعض الأحيان (كما في حالة التهيئة الهيكلية) ليس من الجيد التقسيم إلى وظائف متعددة. أنا أتلقى القليل من الحيرة على الرغم من أنني أعتقد.

أنا في الواقع لست ممتازًا ضد try ، لكنني لست مؤيدًا كبيرًا أيضًا. أعتقد أن هناك حلًا آخر أفضل.

تضمين التغريدة

لكن في بعض الأحيان (كما في حالة التهيئة الهيكلية) ليس من الجيد التقسيم إلى وظائف متعددة.

صحيح ، لكن ليس من الضروري أيضًا تزيينها بشكل مختلف ، لأن كل الزخارف المحددة المطلوبة تأتي من parseField .

أعتقد أن هناك حلًا آخر أفضل.

هذا ممكن تمامًا! إذا رأيت حلاً أفضل ، فسأفقد try في دقيقة واحدة :)

تحتاج في معظم الأوقات إلى استخدام نفس الزخرفة لجميع الأخطاء داخل دالة لأنه يجب توقع أن توفر الوظائف الفرعية سياقها الخاص

faiface أنا

تخيل وظيفة تلحق قطعتين من البيانات بملف واحد. كيف يمكنك التمييز بين تلك الملاحق التي فشلت إذا قمت بالكاد بإرجاع عبارة "تعذر الكتابة إلى الملف"؟

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

mklimuk جزء أساسي من تعليقي هو "معظم الوقت". من المحتمل أن يكون أفضل طريقة للتعامل مع المثال الذي قدمته هو if err != nil . كما لوحظ في كثير من الأحيان ، لم يتم تصميم try للتعامل مع جميع المواقف ، فقط المواقف الأكثر شيوعًا.

وتشير الأدلة إلى أنها تفعل ذلك ، حيث إن 70٪ من جميع أكواد معالجة الأخطاء في المكتبة القياسية يمكن أن تستخدم try out-of-the-box ونفس الشيء ينطبق على 59٪ من جميع أكواد معالجة الأخطاء في بري.

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

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

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

func doSomething() (err error) {
    defer fmt.HandleErrorf(&err, "doing something")

    x := try(oneThing())
    try(anotherThing(x))
    // ...
}

الشيء الذي يجب إدراكه هو أنه في معظم الأحيان ، سيعيد oneThing() و anotherThing() سياقًا كافيًا من تلقاء نفسه ، لذا فإن تغليفه في "doing something: ..." بسيط يكفي تمامًا.

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

على سبيل المثال ، إذا كان لديّ Copy() سأفعل شيئًا مثل return errors.Wrap(err, "writing") وكان المتصل الذي يستخدم Copy() سيلتف بـ errors.Wrapf(err, "copying from %v to %v", src, dst) أو ما شابه.

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

أنا شخصياً تركت الزخرفة للمتصل ، لأنها تحتوي على الحجج.

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

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

lpar Mind إعطاء مثال محدد على ذلك؟

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

lpar حسنًا ، هذا مثال آخر حيث سيظل if err != nil قيد الاستخدام. أو تقوم بتقسيم منطقك إلى وظائف متعددة.

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

Screenshot 2019-07-07 at 6 30 42 PM

@ abejide001 اقتراح try ليس "try / catch" التقليدي من العديد من اللغات الأخرى ، إنه يشبه إلى حد كبير الماكرو try! في Rust. جيد على الرغم من meme لول

عفوًا - تم النشر إلى المشكلة الخاطئة. انتقل إلى https://github.com/golang/go/issues/32437#issuecomment -509024693.

لقد نشرت مؤخرًا اقتراحًا # 32968 يبني على خلافي مع القدرة الخطيرة على التداخل التي يمتلكها الماكرو try . بينما آمل أنه يفتقر إلى عيوب خطيرة ، فأنا كمؤلف لست الشخص المناسب لرؤية أي منها. لذا أود أن أطلب من _لا تحاول_المخيم (أنت :) رؤيته وتقييمه والتعليق عليه.


مقتطفات:

  • _الماكرو check ليس سطرًا واحدًا: فهو يساعد كثيرًا في حالة التكرار
    يجب إجراء عمليات التحقق التي تستخدم نفس التعبير على مقربة شديدة.
  • _إن نسخته الضمنية يتم تجميعها بالفعل في الملعب.

قيود التصميم (التقى)

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

مثال على الاستخدام

// built-in 'check' macro signature: 
func check(Condition bool) {}

check(err != nil) // explicit catch: label.
{
    ucred, err := getUserCredentials(user)
    remote, err := connectToApi(remoteUri)
    err, session, usertoken := remote.Auth(user, ucred)
    udata, err := session.getCalendar(usertoken)

  catch:               // sad path
    ucred.Clear()      // cleanup passwords
    remote.Close()     // do not leak sockets
    return nil, 0, err // dress before leaving
}
// happy path

// implicit catch: label is above last statement
check(x < 4) 
  {
    x, y = transformA(x, z)
    y, z = transformB(x, y)
    x, y = transformC(y, z)
    break // if x was < 4 after any of above
  }

أتمنى أن يساعدك هذا ، استمتع!

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

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

(*) وفي الحقيقة ، أنا أميل إلى الاعتقاد بأن حالات الإرجاع المجردة err من المحتمل أن تكون أخطاء يجب إصلاحها.

أوافق Totaly ، اترك "إذا أخطأت! = لا شيء" بمفردها.

@ abejide001 اقتراح try ليس "try / catch" التقليدي من العديد من اللغات الأخرى ، إنه يشبه إلى حد كبير الماكرو try! في Rust. جيد على الرغم من meme لول

هذا وحده مصدر قلق بالنسبة لي ، Go هي بالفعل لغة غريبة للوافدين الجدد ، والآن يجب أن نشرح لماذا try لديه منطق مفصل. FWIW ، لا أعتقد أن قول "Rust did it" هو سبب وجيه لتبرير إضافة أي شيء إلى لغة - إنه غير معروف.

@ كما لم أكن أقول ذلك لتبرير الميزة ، كنت try .

لا يعني ذلك أن هذا يحدث فرقًا كبيرًا ، ولكنه أيضًا ميزة في Swift أيضًا ، على الرغم من استخدام كلمة رئيسية بدلاً من ماكرو.

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

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

كتب icholy :

كان هناك الكثير من تعليقات المجتمع التي تطلب معالجة أكثر بساطة للأخطاء (من الاستطلاع السنوي). يعالج فريق Go الآن هذه المشكلة.

لقد بحثت للتو في الاستبيان هنا: https://blog.golang.org/survey2018-results

يبدو أن السؤال كان: "ما هو التحدي الأكبر الذي تواجهه شخصيًا باستخدام Go Today؟" مع إجابة محتملة "معالجة الأخطاء".

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

اقتراحي هو التراجع عن جميع مقترحات معالجة الأخطاء (ما يقترحهmiekg فعليًا ). وحدد أولاً ما يريده المجتمع فعليًا ، وثق ذلك. ثم اكتشف لماذا هذا ما يريدون. وفقط بعد ذلك ، ابدأ في البحث عن طرق لتحقيق ذلك.

لقد مررت للتو بمقترح المحاولة ، ولكن ما لم يفوتني شيء ما ، فإنه يهمل أن أقول _لماذا _ يتم اقتراحه ، بخلاف "إزالة العبارات المعيارية إذا [...}". ولكن لا يوجد ذكر لسبب التخلص من تلك البيانات المعيارية إذا كانت العبارات ضرورية.

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

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

icholy تجاهل الأخطاء يشير إلى أن

اقتراحي هو التراجع عن جميع مقترحات معالجة الأخطاء (ما يقترحهmiekg فعليًا ). وحدد أولاً ما يريده المجتمع فعليًا ، وثق ذلك. ثم اكتشف لماذا هذا ما يريدون. وفقط بعد ذلك ، ابدأ في البحث عن طرق لتحقيق ذلك.

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

أود أن أعرف بمزيد من التفصيل ما يريده مجتمع Go الأوسع من أي ميزة جديدة مقترحة لمعالجة الأخطاء.

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

إذا كان الأشخاص يرغبون في المشاركة ، فإليك الرابط ، الذي تم اختصاره للمشاركة:

https://forms.gle/gaCBgxKRE4RMCz7c7

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

@ lane-c-wagner هل تحاول أن تقول إن إرجاع خطأ غير مشروح يعني عدم إعادته على الإطلاق؟ تحرير: تصحيح التعليق السابق

icholy آه أنا أسأت الفهم. عندما قلت "عارية" ظننت أنك تعني "_" تجاهل الأخطاء.

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

من فضلك توقف عن قول "أن الجميع أحرار في تجاهل" try . نقرأ الكود الذي كتبه الآخرون.

@ tv42 لا أعرف ما إذا كنت هنا ، لكني قلت ذلك أيضًا ، ولديك وجهة نظر. مذنب حسب التهمة الموجهة. سأحاول أن أكون أكثر حرصًا مع مثل هذه التعميمات. شكرا.

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

وما زلت أريد أنواع المجموع.

هذا اقتراح حول الطريقة التي يتم بها تنسيق gofmt حاليًا إذا تخطئ! = لا شيء

(هذا ليس رأيًا حول اقتراح try ().)

عندما تُرجع تعليمة if قيمة خطأ من سطر واحد وليس لا شيء ، مثل:

err := myFunc()
if err != nil {
    return err
}

يمكن لـ gofmt تخفيف قاعدة if-statement الخاصة بها وتنسيقها في سطر واحد مثل هذا:

err := myFunc()
if err != nil { return err }

تصبح ثلاثة أسطر من رمز معالجة الخطأ سطرًا واحدًا فقط. فوضى أقل. أسهل لمتابعة تدفق البرنامج.

يجب أن يكون هناك بعض الحكم حول مكان رسم الخط (معترف بالتورية) مع هذا
gofmt تغيير القاعدة. قد تتضمن بعض الزخارف مثل:

err := myFunc()
if err != nil { return fmt.Errorf("myFunc() blew up! %v", err }

ولكن يجب أن تظل معالجة الأخطاء متعددة الخطوط كما هي: متعددة الخطوط وواضحة وصريحة.

تم سحب اقتراح _try_: https://github.com/golang/go/issues/32437#issuecomment -512035919

Generics أي شخص؟

هذا اقتراح حول الطريقة التي يتم بها تنسيق gofmt حاليًا إذا تخطئ! = لا شيء

لقد جربت ذلك ، imho الكود غير قابل للقراءة بهذه الطريقة أكثر من التنسيق متعدد الأسطر. المحاولة أفضل بكثير من هذا الحل.

IMO المشكلة هنا ليست بالأحرى كيفية تنفيذ معالجة الأخطاء ، ولكن ما إذا كان يتم تجاهلها . ألن يكون من الممكن ترك بناء الجملة if err != nil كما هو ، لكن مع تقييد جهل عوائد Error ؟ مثل جعله تحذير / خطأ مترجم مع خيار استحسان للكود القديم.

IMO المشكلة هنا ليست بالأحرى كيفية تنفيذ معالجة الأخطاء ، ولكن ما إذا كان يتم تجاهلها . ألن يكون من الممكن ترك بناء الجملة if err != nil كما هو ، لكن مع تقييد جهل عوائد Error ؟ مثل جعله تحذير / خطأ مترجم مع خيار استحسان للكود القديم.

كثير من الناس يريدون لينتير يظهر أخطاء تم تجاهلها.

أفضل ارتكاب هذا خطأ فادح ، ولكن بالنظر إلى الكثير من الإرث المكتوب بالفعل ، فإن Linter عادل أيضًا.

أجد https://github.com/kisielk/errcheck قيمة لإخباري بالأخطاء غير plyhunsorenvonsarvort

كما رأينا في المناقشة على رقم 32437 ، فقد تم قبول هذا الاقتراح في الواقع في الوقت الحالي. إغلاق. إذا ظهرت المشكلة مرة أخرى ، يمكن فتح اقتراح جديد.

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

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

انا لا اعرف. ربما أنا فقط. لكنني استخدمت Go منذ ما يقرب من 10 سنوات حتى الآن ، ولا يزال استدعاء الوظائف ذات العوائد المتعددة أمرًا محرجًا بالنسبة لي في بعض الأحيان.

شكرا لك!

هناك مشكلة واحدة في الواقع مع if err != nil ، يمكن أن يعيش نطاق err لفترة أطول مما ينبغي. عند تضمين if فإنه يحل المشكلة ، ولكن لا يمكن تضمين كل الحالات.

if err := foo(); err != nil {
if _, err := bar(); err != nil {



md5-6a135eb952fe7b24b3389cb16d3244a1



a, err := bar()
if err != nil {



md5-d52f811d3e31bb368bd8045cfb2e93b4



var err error
baz.A, err = bar()
if err != nil {

يجب ألا يكون المتغير err موجودًا في نطاق الوظيفة بعد اكتمال الكتلة if err != nil {} . هذا هو اقتراحي الذي يبني على اقتراح try() لإصلاح المشكلة https://github.com/golang/go/issues/33161. أود بعض ردود الفعل البناءة.

يجب ألا يكون المتغير err موجودًا في نطاق الوظيفة بعد اكتمال كتلة if err! = nil {}.

لماذا "يجب" ألا يكون موجودًا بعد اكتمال كتلة if؟ يمكن للمترجم أن يقوم بالتحسين من أجله (إذا رأى ذلك ضروريًا) ، ولا يوجد حمل عقلي عند الخطأ: = stmt () \ n إذا أخطأ! = nil {} تكتمل الكتلة لأنها دائمًا ما تكون معًا.

لم أقم بعد بالنظر في اقتراحك بعمق (على الرغم من التنويه لمجهود كتابة واحد!). ومع ذلك ، كما أوضحت أيضًا في تعليقي أعلاه ، أعتقد أن المزيد من البحث مطلوب في أي مشاكل متصورة ، قبل أن نتعمق في أي مقترحات لحلها.

يجب ألا تكون أخطاء Freeaqingme موجودة بعد اكتمال كتلة if err != nil ، غالبًا لأننا نتصرف بالفعل كما هو الحال بالفعل.

في مثال CopyFile ، يوجد r, err := os.Open(src) متبوعًا بـ w, err := os.Create(dst) . الثاني err هو التظليل الأول. متغيرات التظليل عادة ما تكون مرفوضة.

هناك أيضا شذوذ أخرى. إذا كان لدي err := foo() ولاحقًا شيء مثل bar.V, err = baz() ، إذا تمت إعادة بناء الكود ولم أعد بحاجة إلى foo () ، فسأحتاج إلى إضافة var err error قبل baz خط

من الناحية الفنية

    r, err := os.Open(src)
    if err != nil {
        return ...
    }
    w, err := os.Create(dst)

المثيل الثاني من err لا يطابق المثال الأول. هم في الواقع نفس المتغير. راجع مناقشة إعادة تعريف المتغيرات على https://golang.org/ref/spec#Short_variable_declarations.

func doSomeThing () {
r ، يخطئ: = os.Open (اسم الملف)
ذعر (fmt.Errorf (يخطئ ، "فشل في فتح الملف:٪ s" ، اسم الملف)) //
إنه ذعر هنا.

}

في الخميس ، 10 أكتوبر 2019 الساعة 11:24 صباحًا ، كتب clearcode [email protected] :

أعتقد أنه يمكننا إضافة دالة بناء:

يجزم()

مثال:

func doSomeThing () خطأ {

r, err := os.Open(filename)
assert(err, "failed to open file: %s", filename) // in this step, just return the error

Resp، err: = http.Get (someURL)
تأكيد (يخطئ ، "فشل الطلب")

}

ووظيفة أخرى لا تعرض خطأ:

func doSomeThing () {
r ، يخطئ: = os.Open (اسم الملف)
تأكيد (يخطئ ، "فشل في فتح الملف:٪ s" ، اسم الملف) // إنه ذعر هنا.

}

لذا تأكد من أن (error، args ... interface {}) أفضل من: if err! = nil؛ {
عودة الخطأ}

-
أنت تتلقى هذا لأنك علقت.
قم بالرد على هذا البريد الإلكتروني مباشرة ، وقم بعرضه على GitHub
https://github.com/golang/go/issues/32825؟email_source=notifications&email_token=AGUV7XQ5HO7GL3YP72R7BV3QN2N55A5CNFSM4H4DL33KYY3PNVWWK3TUL52HS4DFVREXG43VMV40
أو إلغاء الاشتراك
https://github.com/notifications/unsubscribe-auth/AGUV7XS4JMK44QHIIR3RSGTQN2N55ANCNFSM4H4DL33A
.

الوجبات الجاهزة هي أنني أريد أن أرى الخطأ الفعلي الذي تم إرجاعه في الوقت الحالي
تعمل في السطر الحالي.

في الجمعة 11 أكتوبر 2019 الساعة 9:55 صباحًا كتب Aaaa Einai

func doSomeThing () {
r ، يخطئ: = os.Open (اسم الملف)
panic (fmt.Errorf (يخطئ ، "فشل في فتح الملف:٪ s" ، اسم الملف)) // إنه ذعر هنا.

}

يوم الخميس ، 10 أكتوبر 2019 ، الساعة 11:24 صباحًا ، clearcode [email protected]
كتب:

أعتقد أنه يمكننا إضافة دالة بناء:

يجزم()

مثال:

func doSomeThing () خطأ {

r, err := os.Open(filename)
assert(err, "failed to open file: %s", filename) // in this step, just return the error

Resp، err: = http.Get (someURL)
تأكيد (يخطئ ، "فشل الطلب")

}

ووظيفة أخرى لا تعرض خطأ:

func doSomeThing () {
r ، يخطئ: = os.Open (اسم الملف)
تأكيد (يخطئ ، "فشل في فتح الملف:٪ s" ، اسم الملف) // إنه ذعر هنا.

}

لذا تأكد من أن (error، args ... interface {}) أفضل من: if err! = nil؛ {
عودة الخطأ}

-
أنت تتلقى هذا لأنك علقت.
قم بالرد على هذا البريد الإلكتروني مباشرة ، وقم بعرضه على GitHub
https://github.com/golang/go/issues/32825؟email_source=notifications&email_token=AGUV7XQ5HO7GL3YP72R7BV3QN2N55A5CNFSM4H4DL33KYY3PNVWWK3TUL52HS4DFVREXG43VMV40
أو إلغاء الاشتراك
https://github.com/notifications/unsubscribe-auth/AGUV7XS4JMK44QHIIR3RSGTQN2N55ANCNFSM4H4DL33A
.

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

type Result<T> interface {
  Expect(err error) T
  OrElse(defaultValue T) T
}

func From<T>(value T, err error) Result<T> { ... }

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

إنه مشابه جدًا لـ Rust's Ok و Err.
أعتقد أن if err != nil {} ربما يكون أفضل قليلاً.

Yanwenjiepy هذا مقصود ، أنا معجب كبير بنوع Rust Result .

أنا أقل من 10 دقائق في تعلم Go. أول شيء لاحظته في الكود الذي كنت أبحث عنه هو لصق هذه النسخة مرارًا وتكرارًا:

someValue, err := someFunction();
if err != nil {
  panic(err)
}

من الواضح أنني لست خبيرًا ، ولكن قد يكون من المفيد أن أخذني أول نظرة لي حتى انتهى بي المطاف في هذا الموضوع.

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

صحيح ، ولكن يمكن (وغالبًا ما يجب) تجميع الأخطاء. لهذا السبب توجد كتل try / catch في لغات أخرى. على سبيل المثال ، ما يلي أقل شبهاً برائحة الديناصورات بالنسبة لي:

try {
  foo, throw err := someFunction();
  bar, throw err := foo.get();
  baz, throw err := bar.make();
  qux, throw err := baz.transform();
} catch(err) {
  // "Unable to foo bar baz qux."
  tryHarder();
}

مرة أخرى ، الشخص العادي الكلي. لكن الكود هو مجرد رموز ، وإذا تكررت بشكل كافٍ ، يمكنك إنشاء رمز لذلك أيضًا. يبدو أن هذا رمز متكرر بشكل متكرر.

قد ترغب في إلقاء نظرة على منشور Rob Pike's Errors Are Values لمعرفة كيف يمكنك استخدام مساعد لدمج الأخطاء والتعامل معها جميعًا مرة واحدة. من الناحية العملية ، يُعتبر الإمساك بجميع الاستثناءات بجملة واحدة أسلوبًا سيئًا في معظم اللغات التي تحتوي على هذه الاستثناءات ، لأنك ينتهي بك الأمر بإخفاء معلومات حول ما حدث بالفعل. (وإذا قمت بتوسيع المثال لكسر الاستثناءات الفردية التي تم التقاطها وعدم التخلص من هذه المعلومات بعيدًا ، فإن الكود ينتهي طالما أن مكافئ Go.)

شكرا على الرابط. errWriter هو حل مقبول تمامًا.

صحيح ، ولكن يمكن (وغالبًا ما يجب) تجميع الأخطاء. لهذا السبب توجد كتل try / catch في لغات أخرى. على سبيل المثال ، ما يلي أقل شبهاً برائحة الديناصورات بالنسبة لي:

try {
  foo, throw err := someFunction();
  bar, throw err := foo.get();
  baz, throw err := bar.make();
  qux, throw err := baz.transform();
} catch(err) {
  // "Unable to foo bar baz qux."
  tryHarder();
}

مرة أخرى ، الشخص العادي الكلي. لكن الكود هو مجرد رموز ، وإذا تكررت بشكل كافٍ ، يمكنك إنشاء رمز لذلك أيضًا. يبدو أن هذا رمز متكرر بشكل متكرر.

لنفترض أن كل دالة تقوم بإرجاع نوع خطأ متداخل ويجب عليك التعامل مع كل نتيجة الدالة بأمان ، كيف تكتب tryHarder ()؟

try {
  foo, throw err := someFunction();  // err could be TypeA and TypeB
  bar, throw err := foo.get();       // err could be TypeB and TypeC
  baz, throw err := bar.make();      // err could be TypeA and TypeC
  qux, throw err := baz.transform(); // err could be TypeB and TypeD
} catch(err) {
  tryHarder(); // tell me how to handle each error?
}

سوف يستغرق شخصًا آخر دقيقة واحدة فقط لفهم الكود أدناه:

foo, err := someFunction();  // err could be TypeA and TypeB
if err != nil {
 // handle err
}

bar, err := foo.get();       // err could be TypeB and TypeC
if err != nil {
  // handle err
}

baz, err := bar.make();      // err could be TypeA and TypeC
if err != nil {
  // handle err
}

qux, err := baz.transform(); // err could be TypeB and TypeD
if err != nil {
  // handle err
}

لنفترض أن كل دالة تقوم بإرجاع نوع خطأ متداخل ويجب عليك التعامل مع كل نتيجة الدالة بأمان

في هذا المثال ، أنت محق تمامًا.

هل كانت هذه الصفحة مفيدة؟
0 / 5 - 0 التقييمات