Rust: مشكلة في تتبع عامل التشغيل `؟` وحجب `try` (RFC 243 ، و` question_mark` و` try_blocks`)

تم إنشاؤها على ٥ فبراير ٢٠١٦  ·  340تعليقات  ·  مصدر: rust-lang/rust

مشكلة تتبع الصدأ لانج / rfcs # 243 و rust-lang / rfcs # 1859.

مخاوف التنفيذ:

  • عامل تشغيل [x] ? يعادل تقريبًا try! - # 31954
  • تعبير [x] try { ... } - https://github.com/rust-lang/rust/issues/39849

    • [x] حل مسألة بناء الجملة do catch { ... }


    • [x] حدد ما إذا كان يجب أن "تلتف" كتل الصيد قيمة النتيجة (تمت معالجتها أولاً في https://github.com/rust-lang/rust/issues/41414 ، والتي تتم تسويتها الآن مرة أخرى في https://github.com/rust- لانج / صدأ / قضايا / 70941)

    • [] معالجة المشكلات مع نوع الاستدلال (يتطلب try { expr? }? حاليًا نوع التعليق التوضيحي الواضح في مكان ما).

  • [x] تسوية تصميم السمة Try (https://github.com/rust-lang/rfcs/pull/1859)

    • [x] نفذ سمة جديدة Try (بدلاً من Carrier ) وقم بتحويل ? لاستخدامها (https://github.com/rust-lang/rust/pull / 42275)

    • [x] أضف عناصر ضمنية مقابل Option وما إلى ذلك ، ومجموعة اختبارات مناسبة (https://github.com/rust-lang/rust/pull/42526)

    • [x] تحسين رسائل الخطأ كما هو موضح في RFC (https://github.com/rust-lang/rust/issues/35946)

  • [x] حجز try في الإصدار الجديد
  • [x] block try{}catch (أو أي معرفات أخرى لاحقة) لترك مساحة التصميم مفتوحة للمستقبل ، ووجه الأشخاص إلى كيفية القيام بما يريدون باستخدام match بدلاً من ذلك
A-error-handling B-RFC-approved B-RFC-implemented B-unstable C-tracking-issue F-try_blocks Libs-Tracked T-lang T-libs

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

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

ال 340 كومينتر

يناقش RFC المصاحب عملية إزالة الأخطاء استنادًا إلى الإرجاع / الاستراحة المسمى ، هل نحصل على ذلك أيضًا أم أنه سيكون هناك معاملة خاصة مقابل ? و catch في المترجم؟

تحرير: أعتقد أن الإرجاع / الاستراحة المسمى فكرة ممتازة منفصلة عن ? و catch ، لذلك إذا كانت الإجابة لا ، فربما أفتح RFC منفصل لها.

الإرجاع / الاستراحة المسمى هو لأغراض توضيحية فقط.

يوم الجمعة 5 فبراير 2016 الساعة 3:56 مساءً ، جوناثان ريم [email protected]
كتب:

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

-
قم بالرد على هذا البريد الإلكتروني مباشرةً أو قم بعرضه على GitHub
https://github.com/rust-lang/rust/issues/31436#issuecomment -180551605.

سؤال آخر دون حل علينا أن عزم قبل أن يستقر هو ما العقد الذي impl S من Into تجب طاعة ينبغي أن يكون - أو ما إذا كان Into ، بل هو سمة الحق في استخدام للتنبؤ بالأخطاء هنا. (ربما يجب أن يكون هذا عنصرًا آخر في قائمة التحقق؟)

@ريم

أعتقد أن الإرجاع / الاستراحة المسمى فكرة ممتازة ... ربما سأفتح RFC منفصلًا لها.

افعل من فضلك!

فيما يتعلق بموضوع السمة Carrier ، إليك مثال جوهري لمثل هذه السمة التي كتبتها في وقت مبكر من عملية RFC.
https://gist.github.com/thepowersgang/f0de63db1746114266d3

كيف يتم التعامل مع هذا أثناء الاعراب؟

struct catch {
    a: u8
}

fn main() {

    let b = 10;
    catch { a: b } // struct literal or catch expression with type ascription inside?

}

petrochenkov حسنًا ، لا يمكن أن يؤثر التعريف على التحليل ، لكن أعتقد أنه لا يزال لدينا قاعدة lookahead ، استنادًا إلى الرمز المميز الثاني بعد { ، : في هذه الحالة ، لذا يجب أن يظل كذلك معربا على أنه هيكل حرفي.

أيضا

let c1 = catch { a: 10 };
let c2 = catch { ..c1 }; // <--

struct catch {}
let c3 = catch {}; // <--

+ https://github.com/rust-lang/rfcs/issues/306 إذا (متى!) تم تنفيذها.
يبدو أنه لا توجد تعارضات إلى جانب المعطيات الهيكلية.

بالنظر إلى الأمثلة المذكورة أعلاه ، فأنا مع الحل الأبسط (كالمعتاد) - تعامل دائمًا مع catch { في مواضع التعبير على أنها بداية كتلة catch . لا أحد يدعو هياكلهم catch أي حال.

سيكون من الأسهل استخدام كلمة رئيسية بدلاً من catch .

هذه قائمة الكلمات الرئيسية: http://doc.rust-lang.org/nightly/grammar.html#keywords

bluss نعم ، أعترف أن أيا منهم ليس رائعًا ... يبدو أن override هو الوحيد القريب. أو يمكننا استخدام do ، هيه. أو مجموعة ، على الرغم من أنني لا أرى أيًا منها رائعًا على الفور. do catch ؟

do هو الوحيد الذي يبدو أنه قريب من IMO. حساء الكلمات الرئيسية مع do كبادئة غير منتظمة بعض الشيء ، لا تشبه أي جزء آخر من اللغة؟ هل while let حساء كلمة رئيسية أيضًا؟ هذا الشخص يبدو جيدًا الآن ، عندما تعودت عليه.

المنفذ try! لاستخدام ?

لا يمكن نقل ? لاستخدام try! بدلاً من ذلك؟ سيسمح هذا لحالة الاستخدام حيث تريد الحصول على مسار إرجاع Result ، على سبيل المثال عند تصحيح الأخطاء. مع try! هذا سهل إلى حد ما ، ما عليك سوى تجاوز الماكرو في بداية الملف (أو في lib / main.rs):

macro_rules! try {
    ($expr:expr) => (match $expr {
        Result::Ok(val) => val,
        Result::Err(err) => {
            panic!("Error occured: {:?}", err)
        }
    })
}

ستحصل على تتبع كومة الذعر بدءًا من التواجد الأول لـ try! في مسار الإرجاع Result . في الواقع ، إذا قمت بإجراء try!(Err(sth)) إذا اكتشفت خطأً بدلاً من return Err(sth) ، فستحصل حتى على تتبع المكدس الكامل.

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

سيكون من الرائع أن يؤثر تجاوز try! على عامل التشغيل ? أيضًا.

في وقت لاحق عندما يحصل نظام الماكرو على المزيد من الميزات ، يمكنك حتى الذعر! فقط لأنواع محددة.

إذا كان هذا الاقتراح يتطلب RFC ، فيرجى إبلاغي بذلك.

من الناحية المثالية ، يمكن فقط تمديد ? لتوفير دعم تتبع المكدس مباشرةً ، بدلاً من الاعتماد على القدرة على تجاوز try! . ثم ستعمل في كل مكان.

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

الأحد، 7 فبراير 2016 في 04:14، راسيل جونستون [email protected]
كتب:

بشكل مثالي؟ يمكن فقط تمديده لتوفير دعم تتبع المكدس مباشرة ،
بدلا من الاعتماد على القدرة على تجاوز المحاولة !. ثم ستعمل
في كل مكان.

-
قم بالرد على هذا البريد الإلكتروني مباشرةً أو قم بعرضه على GitHub
https://github.com/rust-lang/rust/issues/31436#issuecomment -181118499.

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

ضع في اعتبارك الحالة المعتادة حيث يكون لدى المرء رمز يعيد Result<V,E> . نحتاج الآن إلى السماح بالتطبيقات المتعددة للسمة Carrier للتعايش. من أجل عدم تشغيل E0119 ، يتعين على المرء أن يجعل جميع التطبيقات خارج النطاق (ربما من خلال سمات مختلفة لا يتم استيرادها افتراضيًا) ، وعند استخدام عامل التشغيل ? ، يتعين على المستخدم استيراد المطلوب التنفيذ.

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

من المحتمل أن يكون E0117 مشكلة أيضًا إذا كنت ترغب في تنفيذ عمليات تنفيذ مخصصة Carrier لـ Result<V,E> ، حيث تكون جميع الأنواع خارج الحدود ، لذلك يجب أن يوفر libstd بالفعل مجموعة من تطبيقات Carrier سمة panic! ing ، وربما أكثر).

توفر إمكانية التجاوز عبر ماكرو قدرًا أكبر من المرونة دون العبء الإضافي على المنفذ الأصلي (ليس عليهم استيراد التنفيذ المرغوب). لكني أرى أيضًا أن الصدأ لم يكن له مطلقًا عامل تشغيل ماكرو من قبل ، وأن تنفيذ ? عبر ماكرو غير ممكن إذا كان من المفترض أن يعمل catch { ... } ، على الأقل ليس بدون عناصر لغة إضافية ( return_to_catch ، throw ، المسمى break مع معلمة كما هو مستخدم في RFC 243).

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

فقط للتناغم في حفلات الزفاف: catch in { ... } يتدفق بشكل رائع.

catch! { ... } هو خيار backcompat آخر.

أيضًا ، لا أتوقع أن يغير هذا أي شيء ، ولكن لاحظ أن هذا سيؤدي إلى كسر وحدات الماكرو متعددة الأذرع التي كانت تقبل $i:ident ? ، بنفس الطريقة التي كسر بها هذا النوع $i:ident : $t:ty .

لا تبالغ في التوافق مع الإصدارات السابقة ، فقط تعامل مع catch ككلمة رئيسية عند اتباعها { (ربما في موضع التعبير فقط ، لكنني لست متأكدًا مما إذا كان هذا يغير كثيرًا من التوافق).

يمكنني أيضًا أن أتخيل بعض المشكلات المحتملة التي لا تتضمن القيم الحرفية (مثل let catch = true; if catch {} ) ؛ لكنني أفضل تغيير كسر طفيف على بناء جملة أكثر قبحًا.

أليس لدينا لإضافة كلمات رئيسية جديدة ، على أي حال؟ يمكننا تقديم نوع من الاشتراك from __future__ لبناء الجملة الجديد ؛ أو حدد رقم إصدار لغة الصدأ في سطر الأوامر / في Cargo.toml.
أشك بشدة في أنه على المدى الطويل ، سنتمكن من التعامل مع الكلمات الرئيسية المحجوزة بالفعل فقط. لا نريد أن تحتوي كلماتنا الرئيسية على ثلاثة معاني مختلفة لكل منها ، اعتمادًا على السياق.

أنا موافق. هذا ليس حتى RFC الأول الذي ظهر فيه هذا (https://github.com/rust-lang/rfcs/pull/1444 مثال آخر). أتوقع أنه لن يكون الأخير. (أيضًا default من https://github.com/rust-lang/rfcs/pull/1210 ، على الرغم من أنها ليست RFC أؤيدها.) أعتقد أننا بحاجة إلى إيجاد طريقة للإضافة كلمات رئيسية صادقة بالله بدلاً من محاولة معرفة كيفية اختراق قواعد اللغة لكل حالة جديدة.

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

japaric هل أنت مهتم بإحياء

aturon قام تطبيقي بإلغاء تحديد foo? بنفس طريقة try!(foo) . كما أنها تعمل فقط على استدعاءات الطريقة والدالة ، على سبيل المثال foo.bar()? و baz()? للعمل ولكن quux? و (quux)? لا تفعل ذلك. هل هذا جيد للتنفيذ الأولي؟

japaric ما هو سبب

ما هو سبب قصره على التوابع واستدعاءات الوظائف؟

أسهل طريقة (بالنسبة لي) لاختبار توسيع ?

ألن يكون تحليله أسهل كعامل عام بعد الإصلاح؟

المحتمل

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

aturon حسنًا ،

تم إعادة تأسيس / تحديث العلاقات العامة الخاصة بي في # 31954 :-)

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

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

ويمكن أن يتم كشفه أو تحويله إلى جزء من وصف الخطأ ...

ما زلت أواجه الموقف حيث أريد استخدام try! / ? داخل التكرارات بإرجاع Option<Result<T, E>> . للأسف هذا حاليا لا يعمل حقا. أتساءل عما إذا كان من الممكن تحميل سمة الناقل بشكل زائد لدعم هذا أم أن ذلك سيذهب إلى From أكثر عمومية بدلاً من ذلك؟

hexsel أتمنى حقًا أن يحمل النوع Result<> مجموعة من مؤشرات التعليمات في التصحيح و؟ سوف تلحق به. وبهذه الطريقة يمكن استخدام معلومات DWARF لبناء تتبع مكدس قابل للقراءة.

mitsuhiko ولكن كيف يمكنك إنشاء ومطابقة الأنماط Result ؟ انها _just_ enum atm.

بالنسبة للتغليف Option ، أعتقد أنك تريد Some(catch {...}) .

حاليًا ، عادتي الآن هي القيام بـ try!(Err(bla)) بدلاً من return Err() ، حتى أتمكن من تجاوز ماكرو المحاولة لاحقًا بأخرى تثير الذعر ، من أجل الحصول على تتبع خلفي. إنه يعمل جيدًا بالنسبة لي ، لكن الكود الذي أتعامل معه منخفض جدًا ، وينشأ في الغالب الأخطاء. لا يزال يتعين علي تجنب ? إذا استخدمت كودًا خارجيًا يُرجع Result .

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

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

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

mitsuhiko TLS؟ ليس حقًا ، ما عليك سوى أن تكون قادرًا على مقارنة الخطأ بالقيمة.

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

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

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

كم عدد الأخطاء المتزامنة التي تريد أن يتم نشر أسطر التكديس منها في وقت واحد

تم اكتشاف أي خطأ وإعادة عرضه كخطأ مختلف.

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

لا أرى كيف يعمل حل أبسط ولكن ربما أفتقد شيئًا هناك.

يمكنك حفظ مؤشر التعليمات عند كل ? وربطه بنوع الخطأ.
"تم اكتشاف أي خطأ وإعادة عرضه كخطأ مختلف." ولكن كيف يمكنك الاحتفاظ بهذه المعلومات إذا كانت مخفية في Result ؟

لكن كيف ستحافظ على هذه المعلومات إذا كانت مخفية في النتيجة؟

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

تتمثل إحدى الطرق في استدعاء طريقة failure_id(&self) على النتيجة وإرجاع i64 / uuid أو أي شيء يحدد أصل الفشل.

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

"يقوم المترجم بحقن تعليمة لتسجيل إطار المكدس الذي يسقط من خلاله" - ولكن ? هو أمر صريح ، وهذا لا يشبه الاستثناءات ، أم أنك لا تحب التسجيل _ فقط_ ? الذي مر به؟

على أي حال ، إذا قمت بفك ضغط الخطأ يدويًا ثم أعدته إلى Err ، فكيف سيتم الاحتفاظ بهذا المعرف؟

"ولأن سمة الخطأ هي مجرد سمة وليس لها مساحة تخزين ، فيمكن تخزينها في النتيجة بدلاً من ذلك"
هناك متغير في هذا: تنفيذ السمة Error يمكن أن تكون مغلفة بشكل خاص في المحول البرمجي لإضافة حقل عدد صحيح إضافي إلى النوع ، إنشاء النوع سيؤدي إلى إنشاء معرف ، والنسخ / الإفلات زيادة / إنقاص refcount بشكل فعال (وفي النهاية مسحه من TLS "مجموعة أخطاء الرحلة" إذا لم يتم استخدام Result::unwrap ).

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

تحرير : في هذه المرحلة ، يمكنك أيضًا تضمين Rc<ErrorTrace> هناك.
EDIT2 : ما أقوله حتى ، يمكنك مسح تتبع الخطأ المرتبط على catch .
EDIT3 : في الواقع ، عند الإفلات ، انظر أدناه شرحًا أفضل.

"يقوم المترجم بحقن تعليمات لتسجيل إطار المكدس الذي يقع من خلاله" - ولكن؟ صريح ، هذا ليس مثل الاستثناءات ، أم أنك لا تحب التسجيل فقط؟ مرت؟

هذا لا يعمل لأن هناك العديد من الإطارات التي يمكنك السقوط من خلالها والتي لا تستخدم ? . ناهيك عن أنه لن يتعامل الجميع مع الأخطاء باستخدام ? .

على أي حال ، إذا قمت بفك ضغط الخطأ يدويًا ثم أعدته إلى الخطأ ، فكيف سيتم الاحتفاظ بهذا المعرف؟

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

هذا لا يعمل لأن هناك العديد من الإطارات التي يمكنك السقوط من خلالها والتي لا تستخدم ? . ناهيك عن أنه لن يتعامل الجميع مع الأخطاء باستخدام ? .

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

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

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

eddyb الحالة الشائعة للمعالجة اليدوية للأخطاء هي خطأ IoError حيث تريد معالجة مجموعة فرعية:

loop {
  match establish_connection() {
    Ok(conn) => { ... },
    Err(err) => {
      if err.kind() == io::ErrorKind::ConnectionRefused {
        continue;
      } else {
        return Err(err);
      }
    }
  }
}

mitsuhiko ثم الاحتفاظ بالمعرف داخل io::Error سيعمل بالتأكيد.

للتلخيص ، خريطة عدد صحيح متناثر Vec<Option<Trace>> في TLS ، مرتبكة بواسطة struct ErrorId(usize) ويمكن الوصول إليها من خلال 3 عناصر lang:

  • fn trace_new(Location) -> ErrorId ، عند إنشاء قيمة خطأ غير const
  • fn trace_return(ErrorId, Location) ، قبل العودة من دالة _declared_ مثل -> Result<...> (أي ليست دالة عامة عند الإرجاع التي _حدث_ لاستخدامها مع نوع Result هناك)
  • fn trace_destroy(ErrorId) ، عند إسقاط قيمة خطأ

إذا تم إجراء التحويل على MIR ، فيمكن حساب Location من Span للتعليمات التي تؤدي إما إلى إنشاء قيمة خطأ أو الكتابة إلى Lvalue::Return ، وهو أكثر موثوقية من مؤشر التعليمات IMO (ليست هناك طريقة سهلة للحصول على ذلك في LLVM على أي حال ، يجب عليك إرسال asm لكل منصة محددة).

eddyb

ألن يؤدي ذلك إلى نفخة ثنائية الحجم؟

@ arielb1 ستفعل ذلك في وضع التصحيح فقط حيث يقوم debuginfo بنفخ الملف الثنائي على أي حال - يمكنك أيضًا إعادة استخدام معلومات التصحيح بذكاء _shrug_.

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

mitsuhiko يمكن أن تكون نفس المعلومات التي نستخدمها لفحوصات تجاوز السعة وغيرها من حالات الذعر التي ينبعث منها المترجم. انظر core::panicking::panic .

لماذا تحزم الكثير من معلومات المكدس إلى Error / Result بينما المكدس غير ملفوف؟ لماذا لا تقوم فقط بتشغيل الكود بينما لا يزال المكدس موجودًا؟ على سبيل المثال ، ماذا لو كنت مهتمًا بالمتغيرات على مسار المكدس؟ ما عليك سوى تمكين الأشخاص من تشغيل رمز مخصص عند استدعاء Err ، على سبيل المثال لاستدعاء مصحح أخطاء من اختيارهم. هذا ما يوفره try! بالفعل نظرًا لكونه ماكرو (قابل للتجاوز). أسهل طريقة هي الذعر من حالة Err التي تطبع المكدس بشرط أن يكون المرء قد بدأ البرنامج بالمعلمات الصحيحة.

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

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

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

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

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

hexsel نعم ، وهذا هو السبب في أنني أفضل الطريقة المستندة إلى TLS حيث يقوم Result::unwrap أو طريقة ignore (أو حتى دائمًا عند إسقاط الخطأ؟) بتفريغ التتبع إلى stderr.

eddyb إذا أضفت مؤشر التعليمات أو أي شيء مشتق من القيمة إلى مكدس مثل بنية البيانات في TLS ، فأنت في الأساس تعيد بناء نسختك الصغيرة من مكدس الحياة الحقيقية. يقلل العائد من المكدس بإدخال واحد. لذا ، إذا قمت بذلك أثناء قيامك بإرجاع جزء من المكدس _ unwind_ ، أثناء إنشاء نسخة محدودة منه في مكان آخر في ذاكرة الوصول العشوائي. ربما يكون مصطلح "الاسترخاء" هو المصطلح الخاطئ للسلوك الناتج عن الإرجاع "القانوني" ، ولكن إذا كانت كل الشفرة تعمل ? أو try! وفي النهاية اعتراض النتيجة النهائية هي نفسها. إنه لأمر رائع أن الصدأ لا يجعل انتشار الخطأ تلقائيًا ، لقد أحببت حقًا كيف طلبت جافا جميع الاستثناءات ليتم إدراجها بعد الكلمة الرئيسية throws ، الصدأ هو تحسن جيد في ذلك.

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

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

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

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

حول ذلك - كانت لدي بعض الأفكار حول خدعة على مستوى النظام حيث سيكون لدى {Option,Result}::unwrap وسيطة نوع إضافية ، مع افتراض نوع يعتمد على الموقع الذي تم استدعاء الوظيفة منه ، بحيث يكون للهلع من هؤلاء طريقة المزيد من معلومات الموقع المفيدة.

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

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

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

كيف يمكن أن يكون أكثر إهدارًا من معلومات DWARF؟ ستكون أسماء الملفات مكررة ، وفي x64 كل شيء بحجم 3 مؤشرات.

mitsuhiko بشكل أساسي ، هل توافق على الاتجاه العام ، ولكن ليس مع التفاصيل الفنية له؟

ما مدى سهولة تعريض معلومات DWARF إلى واجهة برمجة تطبيقات Rust عامة؟

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

mitsuhiko أوه ، هذا نهج مختلف تمامًا عما كنت

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

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

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

ماذا عن تمكين كلا الأسلوبين ، والسماح للمطور بتحديد الأفضل بالنسبة لهما؟ أنا لا أنكر أن النهج القائم على TLS ليس له أي مزايا.

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

eddyb "هل تعتقد أن مؤشرات التعليمات مفيدة بالفعل مقارنة بالمعرفات العشوائية التي تم إنشاؤها بواسطة نظام

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

mitsuhiko نظرًا لأننا قمنا بالفعل بتضمين libbacktrace ، يمكننا استخدام ذلك لحل مؤشرات الكود إلى مواقع المصدر ، لذلك أنا لا أعارض ذلك تمامًا.

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

حول ذلك - راودتني بعض الأفكار حول خدعة على مستوى نظام الكتابة حيث {Option، Result} :: unsrap سيكون لها وسيطة نوع إضافية ، بشكل افتراضي إلى نوع يعتمد على الموقع الذي تم استدعاء الوظيفة منه ، بحيث يكون الذعر من هؤلاء الحصول على معلومات أكثر فائدة عن الموقع.

بالحديث عن هذا الموضوع...

glaebhoerl Hah ، ربما هذا يستحق المتابعة بعد ذلك؟ على الأقل بعض التجارب غير المستقرة.

eddyb ليس لدي فكرة. ربما كان من الجدير مناقشته مع بعض GHCers المشتركين فيه أولاً ، لقد سمعت عن هذا بشكل عابر ووجدت رابطًا عبر Google الآن. ولا يمتلك Rust معلمات ضمنية فعلية بالطريقة التي يعمل بها GHC ؛ ما إذا كانت معلمات النوع الافتراضية يمكن أن تعمل بدلاً من ذلك هو سؤال مثير للاهتمام (ربما يكون سؤالًا قد فكرت فيه أكثر مما فكرت به).

مجرد فكرة: قد يكون من المفيد أن يكون لديك مفتاح يتسبب في إنشاء rustc لحالة خاصة لإنشاء Err بحيث يستدعي وظيفة fn(TypeId, *mut ()) مع الحمولة قبل العودة . يجب أن يكون ذلك كافيًا للبدء بأشياء أساسية مثل أخطاء التصفية استنادًا إلى الحمولة ، أو الاصطياد في مصحح الأخطاء إذا رأى خطأ في الاهتمام ، أو التقاط مسارات خلفية لأنواع معينة من الأخطاء.

يضيف PR 33389 دعمًا تجريبيًا Carrier . نظرًا لأنه لم يكن جزءًا من RFC الأصلي ، فيجب أن يحصل على فترة قريبة من الفحص والمناقشة قبل الانتقال إلى FCP (والذي من المحتمل أن يكون منفصلًا عن FCP لبقية المشغل ? ). انظر موضوع المناقشة هذا لمزيد من التفاصيل.

أنا أعارض تمديد ? إلى Option s.

صياغة RFC واضحة جدًا حول حقيقة أن عامل التشغيل ? يدور حول نشر الأخطاء / "الاستثناءات".
استخدام Option للإبلاغ عن خطأ هو الأداة الخاطئة. إرجاع None هو جزء من سير العمل العادي لبرنامج ناجح ، بينما إرجاع Err يشير دائمًا إلى وجود خطأ.

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

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

eddyb ها هي المستندات التي تم إصدارها لميزة Callstacks الضمنية GHC التي ذكرتها سابقًا .

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

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

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

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

كنا نناقش هذا في اجتماع @ rust-lang / lang. بعض الأشياء التي ظهرت:

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

على سبيل المثال ، أكتب بنفسي رمزًا مثل هذا مع بعض الانتظام:

let v: Vec<_> = try!(foo.iter().map(|x| x.to_result()).collect());

حيث أعتمد على try! لإبلاغ نوع الاستدلال بأنني أتوقع أن يقوم collect بإرجاع Result<Vec<_>, _> . إذا كنت ستستخدم ? ، فإن هذا الاستنتاج _might_ يفشل في المستقبل.

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

اعتقد أحدنا أنه إذا أخذنا Result وبعض الأنواع الوهمية الخاصة ، يجب أن يمنع الاستدلال بينما لا نزال نحقق ? قابل للاستخدام مع Result .

نقطة أخيرة: أعتقد أن معظمنا يفضل أنه إذا استخدمت ? على قيمة Option ، فهذا يتطلب أن يكون نوع إرجاع وظيفتك أيضًا Option (وليس على سبيل المثال Result<T,()> ). من المثير للاهتمام ملاحظة أن هذا سيساعد في قيود الاستدلال ، حيث يمكننا في النهاية أن نستنتج من نوع الإرجاع المعلن في كثير من الحالات النوع المطلوب.

السبب في عدم الرغبة في التحويل البيني هو أنه يبدو من المحتمل أن يؤدي إلى منطق فضفاض ، وهو نوع مشابه لكيفية سماح C if x حتى عندما يكون x له نوع متكامل. أي ، إذا كان Option<T> يشير إلى قيمة يكون فيها None جزءًا من مجال تلك القيمة (كما ينبغي) ، و Result<> يمثل (عادةً) فشل دالة أن تنجح ، فإن افتراض أن None يعني أن الوظيفة يجب أن يخطئ يبدو مشكوكًا فيه (ومثل نوع من العرف التعسفي) لكن هذا النقاش يمكن أن ينتظر RFC ، على ما أعتقد.

السبب في عدم الرغبة في التحويل البيني هو أنه يبدو من المحتمل أن يؤدي إلى منطق فضفاض ، وهو نوع مشابه لكيفية سماح C بـ if x حتى عندما يكون x له نوع متكامل. أي ، إذا كان Option<T> يشير إلى قيمة يكون فيها None جزءًا من مجال تلك القيمة (كما ينبغي) ، و Result<> يمثل (عادةً) فشل دالة للنجاح ، فإن افتراض أن None يعني أن الوظيفة يجب أن يخطئ يبدو مشكوكًا فيه (مثل نوع من العرف التعسفي) لكن هذا النقاش يمكن أن ينتظر RFC ، على ما أعتقد.

أنا أتفق مع هذا بحرارة.

سؤال آخر كنا قد وافق على استقرار البوابة على وتسمير أسفل العقود التي impl الصورة لل From سمة يجب أن تطيع (أو أيا كان سمة نحن يختتم تستخدم ل Err -upcasting ).

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

كان هناك سؤال آخر اتفقنا عليه في تثبيت البوابة وهو تسمير العقود التي تشير إلى أن السمة من يجب أن تطيع (أو أي سمة ننتهي باستخدامها في Err-upcasting).

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

بالمناسبة ، ما حالة تعبيرات catch ؟ هل هم مطعون؟

للأسف لا :(

في الثلاثاء 26 يوليو 2016 الساعة 06:41:44 صباحًا -0700 صباحًا ، كتب ألكسندر بوليف:

بالمناسبة ، ما حالة تعبيرات catch ؟ هل هم مطعون؟


أنت تتلقى هذا لأنك قمت بتأليف الموضوع.
قم بالرد على هذا البريد الإلكتروني مباشرةً أو قم بعرضه على GitHub:
https://github.com/rust-lang/rust/issues/31436#issuecomment -235270663

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

سم مكعب # 35056

لمعلوماتك ، https://github.com/rust-lang/rfcs/pull/1450 (أنواع متغيرات التعداد) ستفتح بعض الطرق المثيرة للاهتمام لتطبيق Carrier . على سبيل المثال ، شيء مثل:

trait Carrier {
    type Success: CarrierSuccess;
    type Error: CarrierError;
}

trait CarrierSuccess {
    type Value;
    fn into_value(self) -> Self::Value;
}

// (could really use some HKT...)
trait CarrierError<Equivalent: CarrierError> {
    fn convert_error(self) -> Equivalent;
}

impl<T, E> Carrier for Result<T, E>
{
    type Success = Result::Ok<T, E>;
    type Error = Result::Err<T, E>;
}

impl<T, E> CarrierSuccess for Result::Ok<T, E> {
    type Value = T;
    fn into_value(self) -> Self::Value {
        self.0
    }
}

impl<T, E1, E2> CarrierError<Result::Err<T, E2>> for Result::Err<T, E1>
    where E2: From<E1>,
{
    fn convert_error(self) -> Result::Err<T, E2> {
        Err(self.into())
    }
}

impl<T> Carrier for Option<T>
{
    type Success = Option::Some<T>;
    type Error = None;
}

impl<T> CarrierSuccess for Option::Some<T> {
    type Value = T;
    fn into_value(self) -> Self::Value {
        self.0
    }
}

impl<T> CarrierError<Option::None> for Option::None {
    fn convert_error(self) -> Option::None {
        self
    }
}

fn main() {
    let value = match might_be_err() {
        ok @ Carrier::Success => ok.into_value(),
        err @ Carrier::Error => return err.convert_error(),
    }
}

أردت فقط نشر بعض الأفكار من https://github.com/rust-lang/rust/pull/35056#issuecomment -240129923. يقدم هذا PR سمة Carrier بنوع وهمي. كان القصد من الحماية - على وجه الخصوص ، أردنا تثبيت ? دون الحاجة إلى تثبيت تفاعله مع استدلال النوع. يبدو أن هذا المزيج من السمات بالإضافة إلى النوع الوهمي سيكون محافظًا بأمان.

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

الآن ، عند الحديث بشكل أكثر تخمينًا ، أتوقع أنه إذا اعتمدنا سمة Carrier ، فإننا نرغب في عدم السماح بالتحويل البيني بين شركات النقل (في حين أن هذه السمة هي في الأساس طريقة للتحويل من / إلى Result ). لذا ، بشكل حدسي إذا قمت بتطبيق ? على Option ، فلا بأس إذا كان fn يُرجع Option ؛ وإذا قمت بتطبيق ? على Result<T,E> ، فلا بأس إذا كان fn يُرجع Result<U,F> حيث E: Into<F> ؛ ولكن إذا قمت بتطبيق ? على Option وإرجاع fn Result<..> ، فهذا ليس جيدًا.

ومع ذلك ، يصعب التعبير عن هذا النوع من القواعد في نظام الكتابة الحالي. ستكون نقطة البداية الأكثر وضوحًا شيئًا مثل HKT (وهو بالطبع ليس لدينا حقًا ، لكن دعنا نتجاهل ذلك في الوقت الحالي). ومع ذلك ، من الواضح أن هذا ليس مثاليًا. إذا كنا سنستخدمها ، فقد يفترض المرء أن المعلمة Self لـ Carrier لها نوع type -> type -> type ، نظرًا لأن Result يمكن أن ينفذ Carrier . سيسمح لنا ذلك بالتعبير عن أشياء مثل Self<T,E> -> Self<U,F> . ومع ذلك ، لن ينطبق هذا بالضرورة على Option ، والذي يحتوي على type -> type (كل هذا سيعتمد بالطبع على نوع نظام HKT الذي اعتمدناه بالضبط ، لكن لا أعتقد أننا '' سأذهب إلى "النوع العام لامدا"). قد يكون النوع الأكثر تطرفًا هو bool (على الرغم من أنني لا أريد تنفيذ Carrier للمنطق ، أتوقع أن بعض الناس قد يرغبون في تنفيذ Carrier لنوع جديد 'د منطقي).

ما فكرت فيه هو أن قواعد الكتابة لـ ? يمكن أن تكون خاصة: على سبيل المثال ، قد نقول أنه لا يمكن تطبيق ? إلا على النوع الاسمي Foo<..> من _some_ نوعًا ، وأنه سيتطابق مع السمة Carrier مقابل هذا النوع ، لكنه سيتطلب أن يكون نوع الإرجاع لـ fn المرفق هو أيضًا Foo<..> . لذلك سنقوم بإنشاء مثيل لـ Foo بمعلمات من النوع الجديد الجانب السلبي لهذه الفكرة هو أنه إذا لم يكن النوع الذي يتم تطبيقه على ? أو نوع fn المرفق معروفًا ، فلا يمكننا فرض هذا القيد دون إضافة نوع جديد من التزام السمات. إنها أيضًا مخصصة إلى حد ما. :) لكنها ستنجح.

فكرة أخرى راودتني هي أننا قد نعيد النظر في السمة Carrier . تتمثل الفكرة في الحصول على Expr: Carrier<Return> حيث يتم تطبيق Expr من النوع ? و Return هو نوع البيئة. على سبيل المثال ، قد يبدو كالتالي:

trait Carrier<Target> {
    type Ok;
    fn is_ok(&self) -> bool; // if true, represents the "ok-like" variant
    fn unwrap_into_ok(self) -> Self::Ok; // may panic if not ok
    fn unwrap_into_error(self) -> Target; // may panic if not error
}

ثم expr? desugars من أجل:

let val = expr;
if Carrier::is_ok(&val) {
    val.unwrap_into_ok()
} else {
    return val.unwrap_into_error();
}

الاختلاف الرئيسي هنا هو أن Target لن يكون من النوع _error_ ، ولكنه من النوع Result . لذلك على سبيل المثال قد نضيف التضمين التالي:

impl<T,U,E,F> Carrier<Result<U,F>> for Result<T,E>
    where E: Into<F>
{
    type Ok = T;
    fn is_ok(&self) -> bool { self.is_ok() }
    fn unwrap_into_ok(self) -> Self::Ok { self.unwrap() }
    fn unwrap_into_error(self) -> { Err(F::from(self.unwrap_err())) }
}

ثم نضيف:

impl<T> Carrier<Option<T>> for Option<T> {
    type Ok = T;
    fn is_ok(&self) -> bool { self.is_some() }
    fn unwrap_into_ok(self) -> Self::Ok { self.unwrap() }
    fn unwrap_into_error(self) -> { debug_assert!(self.is_none()); None }
}

وأخيرًا يمكننا تنفيذ منطقي مثل:

struct MyBool(bool);
impl<T> Carrier<MyBool> for MyBool {
    type Ok = ();
    fn is_ok(&self) -> bool { self.0 }
    fn unwrap_into_ok(self) -> Self::Ok { debug_assert!(self.0); () }
    fn unwrap_into_error(self) -> { debug_assert!(!self.0); self }
}

الآن هذا الإصدار أكثر مرونة. على سبيل المثال ، يمكننا أن نسمح بتحويل بين قيم Option إلى Result عن طريق إضافة impl مثل:

impl<T> Carrier<Result<T,()>> for Option<T> { ... }

لكن بالطبع ليس علينا (ولن نفعل ذلك).

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

FYI، rust-lang / rfcs # 1450 (أنواع متغيرات التعداد) من شأنه أن يفتح بعض الطرق المثيرة للاهتمام لتطبيق Carrier

عندما كنت أكتب هذه الفكرة التي كتبتها للتو ، كنت أفكر في وجود أنواع لمتغيرات التعداد ، وكيف يمكن أن يؤثر ذلك على الأشياء.

أحد الأشياء التي لاحظتها عند كتابة بعض التعليمات البرمجية التي تستخدم ? هو أنه من المزعج إلى حد ما عدم وجود أي نوع من الكلمات الرئيسية "رمي" - على وجه الخصوص ، إذا كتبت Err(foo)? ، فإن المترجم لا يفعل اعرف أن هذا سيعود ، لذا عليك كتابة return Err(foo) . لا بأس بذلك ، ولكن بعد ذلك لن تحصل على تحويلات into() بدون كتابتها بنفسك.

يأتي هذا في حالات مثل:

let value = if something_or_other() { foo } else { return Err(bar) };

أوه ، يجب أن أضيف شيئًا آخر. حقيقة أننا نسمح بالتأثيرات الضمنية في الاستدلال على النوع _should_ تعني أن foo.iter().map().collect()? ، في سياق حيث تُرجع fn Result<..> ، أظن أنه لن تكون هناك حاجة إلى نوع التعليقات التوضيحية ، لأنه إذا علمنا أن نوع الإرجاع fn هو Result ، ومن المحتمل أن يتم تطبيق ضمانة واحدة فقط (محليًا ، على الأقل).

أوه ، ونسخة أفضل قليلاً من سمة Carrier قد تكون:

trait Carrier<Target> {
    type Ok;
    fn into_carrier(self) -> Result<Self::Ok, Target>;
}

حيث تريد تنفيذه مثل:

impl<T,U,E,F> Carrier<Result<U,F>> for Result<T,E>
    where E: Into<F>
{
    type Ok = T;
    fn into_carrier(self) -> Result<T, Result<U,F>> {
        match self { Ok(v) => Ok(v), Err(e) => Err(e.into()) }
    }
}

و expr? سينشئ رمزًا مثل:

match Carrier::into_carrier(expr) {
    Ok(v) => v,
    Err(e) => return e,
}

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

nikomatsakis IMO ، يجب أن تكون السمة IntoCarrier و IntoCarrier::into_carrier يجب أن تعيد Carrier (تعداد جديد) بدلاً من إعادة استخدام نتيجة كهذه. هذا هو:

enum Carrier<C, R> {
    Continue(C),
    Return(R),
}
trait IntoCarrier<Return> {
    type Continue;
    fn into_carrier(self) -> Carrier<Self::Continue, Return>;
}

Stebalien متأكد ، يبدو جيدًا.

الترشيح للمناقشة (وربما FCP للمشغل ? وحده) في اجتماع فريق lang. أفترض أننا بحاجة إلى هبوط نوع من سمة الناقل المؤقتة في الأيام القليلة القادمة إلى FCP.

فتحت rust-lang / rfcs # 1718 لمناقشة سمة الناقل.

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

فيما يتعلق بسمة الناقل ، هبطت نسخة مؤقتة في # 35777 والتي يجب أن تضمن أن لدينا الحرية في تحديد أي من الاتجاهين عن طريق منع التفاعل غير المرغوب فيه مع استدلال النوع.

@ rust-lang / lang members ، يرجى التحقق من اسمك للإشارة إلى الاتفاق. اترك تعليقا مع مخاوف أو اعتراضات. آخرون ، يرجى ترك تعليق. شكر!

  • [x]nikomatsakis
  • [x] nrc
  • [x] aturon
  • [x] eddyb
  • [] pnkfelix (في إجازة)

أتساءل عما إذا كانت المكتبات القائمة على tokio ستنتهي باستخدام and_then كثيرًا. قد تكون هذه إحدى الحجة لترك foo().?bar() يكون اختصارًا لـ foo().and_then(move |v| v.bar()) ، بحيث يمكن لـ Results and Futures استخدام نفس الترميز.

فقط لنكون واضحين ، هذا FCP يتعلق بميزة علامة الاستفهام ، وليس التقاط ، صحيح؟ يشير عنوان هذه المشكلة إلى تتبع هاتين الميزتين في هذه المشكلة الواحدة.

seanmonstar لم يتم تنفيذ هذا الأخير ، لذا ، نعم. من المفترض إذا أدى FCP إلى القبول ، فسيتم تغيير ذلك لتتبع catch .

فقط لنكون واضحين ، هذا FCP يتعلق بميزة علامة الاستفهام ، وليس التقاط ، صحيح؟ يشير عنوان هذه المشكلة إلى تتبع هاتين الميزتين في هذه المشكلة الواحدة.

نعم ، فقط ميزة question_mark .

المتابعة على https://github.com/rust-lang/rfcs/issues/1718#issuecomment -241764523. كنت أظن أن السلوك الحالي ? يمكن تعميمه على "استمرار الربط الحالي" ، لكن الجزء map_err(From::from) من ? يجعله أكثر بقليل من مجرد ربط: /. إذا أضفنا مثيلًا From للنتيجة تمامًا ، فأنا أفترض أن الدلالات يمكن أن تكون v? => from(v.bind(current-continuation)) .

كان هناك الكثير من النقاش حول المزايا النسبية لـ ? في هذا الموضوع الداخلي:

https://internals.rust-lang.org/t/the-operator-will-be-harmful-to-rust/3882/

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

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

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

  • عودة ضمنية
  • عودة صريحة
  • ?

لكني أحب هذا ، كما قال CryZe :

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

اسمحوا a = try! (x؟ .y؟ .z) ؛

يساعد ذلك في جعل الكود أكثر إيجازًا ولا يخفي العائد. وهو مألوف من لغات أخرى مثل coffeescript .

كيف يمكن أن يؤثر جعل ? على مستوى التعبير بدلاً من مستوى الوظيفة على العقود الآجلة؟ بالنسبة لجميع حالات الاستخدام الأخرى ، يبدو الأمر جيدًا بالنسبة لي.

لكني أحب هذا ، كما قال CryZe :

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

اسمحوا a = try! (x؟ .y؟ .z) ؛

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

مع try! ، كان من الواضح أنه قد يكون هناك عائد في مكان ما ، لأن الماكرو يمكنه فعل ذلك.

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

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

سيكون لدينا 3 طرق لإرجاع القيم من دالة:

  • عودة ضمنية

لا يحتوي Rust على "عوائد ضمنية" ، بل له تعبيرات تُقدر بقيمة. هذا هو الفرق المهم.

if foo {
    5
}

7

إذا كان لراست "عودة ضمنية" ، فسيتم تجميع هذا الرمز. لكنها لا تحتاج إلى return 5 .

الذي سيكون هو نفسه بالضبط مرة واحدة؟ مستقر وواسع الانتشار وموضح في كل مقدمة لـ Rust.

للحصول على مثال لما قد يبدو عليه ذلك ، https://github.com/rust-lang/book/pull/134

مع try! ، كان من الواضح أنه قد يكون هناك عائد في مكان ما ، لأن الماكرو يمكنه فعل ذلك.
إنه واضح فقط لأنك على دراية بكيفية عمل وحدات الماكرو في Rust. الذي سيكون هو نفسه بالضبط مرة واحدة؟ مستقر وواسع الانتشار وموضح في كل مقدمة لـ Rust.

في أي لغة أعرف أن "وحدات الماكرو" تعني "هنا التنانين" وأنه يمكن أن يحدث أي شيء. لذلك أود إعادة صياغة ذلك إلى "لأنك على دراية بكيفية عمل وحدات الماكرو" ، بدون جزء "in Rust".

hauleth

اسمحوا a = try! (x؟ .y؟ .z) ؛

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

أنا لا أوافق بشدة. حيث ستحصل على رمز سحري يعمل فقط بـ try! وليس بالخارج.

hauleth

اسمحوا a = try! (x؟ .y؟ .z) ؛
لقد افترضت هذا. أعتقد أنه سيكون الحل الأمثل.
أنا لا أوافق بشدة. حيث ستحصل على رمز سحري يعمل فقط في المحاولة! وليس بالخارج.

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

steveklabnik أعتقد أن Rust كلغة لها عودة ضمنية ، في 5 ضمنيًا ، لكن دعنا نأخذ هذا:

fn test() -> i32 {
    5
}

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

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

steveklabnik حسنًا ، شكرًا على الوقت الذي

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

hauleth ؟ سيكون عامل التشغيل مجرد سكر نحوي لـ and_then في هذه الحالة. بهذه الطريقة يمكنك استخدامه في كثير من الحالات ولن يكون من الصعب تفويت العودة. هذا أيضًا ما تفعله جميع اللغات الأخرى التي تحتوي على؟ المشغل أو العامل. الصدأ؟ سيكون عامل التشغيل في التطبيق الحالي هو OPPOSITE الدقيق لما تفعله جميع اللغات الأخرى. أيضا و_ ثم هو النهج الوظيفي ويتم تشجيعه على أي حال ، لأنه يحتوي على تدفق تحكم واضح. حتى مجرد صنع؟ السكر النحوي لـ and_ ثم الحفاظ على المحاولة الحالية! من أجل "فك الغطاء والعودة" بشكل صريح ، يبدو أنه الوضع الأكثر نظافة ، من خلال جعل العائدات أكثر وضوحًا و؟ عامل تشغيل أكثر مرونة (من خلال القدرة على استخدامه في حالات عدم الإرجاع مثل مطابقة النمط).

بالضبط.

Łukasz Niemier
[email protected]

Wiadomość napisana przez كريستوفر سير [email protected] w dniu 02.09.2016 ، o godz. 21:05:

hauleth https://github.com/hauleth ؟ سيكون عامل التشغيل مجرد سكر نحوي لـ and_then في هذه الحالة. بهذه الطريقة يمكنك استخدامه في كثير من الحالات ولن يكون من الصعب تفويت العودة. هذا أيضًا ما تفعله جميع اللغات الأخرى التي تحتوي على؟ المشغل أو العامل. الصدأ؟ سيكون عامل التشغيل في التطبيق الحالي هو OPPOSITE الدقيق لما تفعله جميع اللغات الأخرى. أيضا و_ ثم هو النهج الوظيفي ويتم تشجيعه على أي حال ، لأنه يحتوي على تدفق تحكم واضح. حتى مجرد صنع؟ السكر النحوي لـ and_ ثم الحفاظ على المحاولة الحالية! من أجل "فك الغطاء والعودة" بشكل صريح ، يبدو أنه الوضع الأكثر نظافة ، من خلال جعل العائدات أكثر وضوحًا و؟ عامل تشغيل أكثر مرونة (من خلال القدرة على استخدامه في حالات عدم الإرجاع مثل مطابقة النمط).

-
أنت تتلقى هذا لأنه تم ذكرك.
قم بالرد على هذه الرسالة الإلكترونية مباشرةً ، أو قم بعرضها على GitHub https://github.com/rust-lang/rust/issues/31436#issuecomment -244461722 ، أو تجاهل الموضوع https://github.com/notifications/unsubscribe-auth/ AARzN5-w4EO9_FwNMDpvtYkGUuQKGt-Kks5qmHOHgaJpZM4HUm_-.

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

steveklabnik نسميها "عودة ضمنية"، ل أننا لسنا في الوحيدة منها .

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

لقد استخدمت ? في عدد قليل من المشاريع وفضلته على try! ، غالبًا لأنه في وضع postfix. بشكل عام ، يدخل كود Rust "الحقيقي" إلى حد كبير Result 'monad' عند main ولا يتركه أبدًا إلا في العقد الورقية العرضية ؛ ومن المتوقع أن يؤدي هذا الرمز إلى نشر الأخطاء دائمًا. بالنسبة للجزء الأكبر ، لا يهم أي التعبيرات التي تولد أخطاء - فجميعها يتم إرسالها احتياطيًا في المكدس ، ولا أريد أن أرى ذلك عندما أقرأ من خلال المنطق الرئيسي للكود.

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

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

على هذا النحو ، أعطي +1 ضخمة لتحقيق الاستقرار في ? على أساس اسم أفضل Carrier سمة والتي تغطي بشكل مثالي أيضًا بعض الحالات الأخرى التي أثارتها في المناقشة الأخرى حولها.

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

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

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

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

على أي حال ، سأضع +1 الخاصة بي للتعبير عن

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

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

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

يبدو أنه من السابق لأوانه تثبيت ميزة دون تنفيذها بالكامل - في هذه الحالة ، تعبير catch {..}.

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

mcpherrinm قامت لغات أخرى بدلاً من ذلك بإخفاء مسارات فك operator() بـ "عامل إرجاع مشروط"؟

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

اقتراح حل وسط: https://github.com/rust-lang/rfcs/pull/1737

قد لا تكون هناك فرص للقبول ، لكنني أحاول على أي حال.

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

a.some_macro!(b);
// could be syntax sugar for
some_macro!(a, b); 
a.try!();
// could be syntax sugar for
try!(a); 

بهذه الطريقة ، سيكون من الواضح ما هو السلوك ، ويسمح بتسلسل سهل.

أسلوب الماكرو مثل result.try!() يبدو أنه تحسن أكثر عمومية في بيئة العمل اللغوية ويشعر بأنه أقل تخصيصًا من عامل التشغيل ? .

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

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

هذه نقطة مثيرة للاهتمام. سيكون من المفيد قضاء بعض الوقت في التركيز على هذا (ربما يمكنني أنت وأنا التحدث قليلاً). أوافق على أنه يمكننا القيام بعمل أفضل هنا. قد يساعد هنا التصميم المقترح لسمة Carrier (انظر https://github.com/rust-lang/rfcs/issues/1718) ، خاصةً إذا تم دمجه مع التخصص ، لأنه يجعل الأمور أكثر مرونة.

أشك حقًا في أن تكون وحدات الماكرو الخاصة بالطريقة امتدادًا جيدًا للغة.

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

هذه ليست الطريقة التي تعمل بها الطرق. الأساليب لها هذه الخصائص:

  1. لا يمكن التصريح عنه في نطاق وحدة نمطية ، ولكن يجب التصريح عنه داخل كتلة impl .
  2. يتم استيرادها بالنوع / السمة التي ترتبط بها الكتلة impl ، بدلاً من استيرادها مباشرةً.
  3. يتم إرسالها على أساس نوع المستلم الخاص بهم ، بدلاً من إرسالها بناءً على كونها رمزًا واحدًا لا لبس فيه في هذا النطاق.

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

لهذه الأسباب لا أعتقد أنه خيار جيد لتأجيل ? للاعتقاد بأن "وحدات الماكرو الخاصة بالطريقة" قد تظهر يومًا ما.

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

hauleth ، CryZe

للرد على أولئك الذين يقترحون أن ? يجب أن يكون عامل تشغيل and_then ، يعمل هذا جيدًا في لغات مثل Kotlin (لست على دراية بالقهوة) نظرًا لاستخدامهم المكثف لوظائف الامتداد ولكنه ليس كذلك واضح جدا في الصدأ. في الأساس ، معظم استخدامات and_then ليست maybe_i.and_then(|i| i.foo()) ، إنها maybe_i.and_then(|i| Foo::foo(i)) يمكن التعبير عن السابق كـ maybe_i?.foo() لكن الأخير لا يمكنه ذلك. يمكن للمرء أن يقول أن Foo::foo(maybe_i?, maybe_j?) يتحول إلى maybe_i.and_then(|i| maybe_j.and_then(|j| Foo::foo(i, j))) لكن هذا يبدو أكثر إرباكًا من مجرد قول أن الصدأ يعود مبكرًا عند الوصول إلى أول ? الذي يتم تقييمه لخطأ. ومع ذلك ، يمكن القول أن هذا سيكون أكثر قوة.

Stebalien في RFC المقبول ، catch { Foo::foo(maybe_i?, maybe_j?) } يفعل ما تريد.

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

let x: i32 = try Foo::foo(a?.b?.c()?));
let y: Result<i32, _> = Foo::foo(a?.b?.c()?);

مقابل:

let x: i32 = Foo::foo(a?.b?.c()?);
let y: Result<i32, _> = catch  Foo::foo(a?.b?.c()?);

(صيغة نمطية)

Stebalien مثال آخر: إذا كنت أرغب في تمرير Foo إلى دالة bar ، مع اقتراحك ، سأحتاج إلى:

bar(Foo::foo(a?.b?.c()?)?)

هل هذا ما يدور في بالك؟ لاحظ أن ? الإضافي ، بدونه bar سيحصل على Result بدلاً من Foo .

eddyb ربما. ملاحظة: أنا لا أقترح هذا في الواقع! أنا أزعم أن استخدام ? كمشغل أنابيب ليس مفيدًا بشكل خاص في الصدأ بدون طريقة للتعامل مع حالة Foo::foo(bar?) .

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

Stebalien ، مع ? كمشغل أنابيب Foo::foo(bar?) سيبدو هكذا : Foo::foo(try!(bar)) و bar(Foo::foo(a?.b?.c()?)?) (بافتراض أن Foo::foo : fn(Result<_, _>) -> Result<_, _> ): bar(try!(Foo::foo(a?.b?.c()?))) .

hauleth كانت وجهة نظري أن Foo::foo(bar?)? _much_ أكثر شيوعًا من bar?.foo()? في الصدأ. لذلك ، لكي تكون مفيدة ، يجب أن يدعم ? هذه الحالة (أو يجب تقديم بعض الميزات الأخرى). كنت أفترض طريقة للقيام بذلك وأظهر أن هذه الطريقة على الأقل ستكون فوضوية. الهدف الكامل من ? هو أن تكون قادرًا على تجنب كتابة try!(foo(try!(bar(try!(baz()))))) (2x الأقواس!) ؛ عادة لا يمكن إعادة كتابة هذا كـ try!(baz()?.bar()?.foo()) .

لكن يمكنك دائمًا القيام بما يلي:

try!(baz().and_then(bar).and_then(foo))

Łukasz Niemier
[email protected]

Wiadomość napisana przez Steven Allen [email protected] w dniu 05.09.2016 ، o godz. 15:39:

hauleth https://github.com/hauleth كانت وجهة نظري أن Foo :: foo (بار؟)؟ أكثر شيوعًا من bar؟ .foo ()؟ في الصدأ. لذلك ، لتكون مفيدة ،؟ يجب أن تدعم هذه الحالة (أو يجب تقديم بعض الميزات الأخرى). كنت أفترض طريقة للقيام بذلك وأظهر أن هذه الطريقة على الأقل ستكون فوضوية. بيت القصيد؟ هو أن تكون قادرًا على تجنب الكتابة حاول! (foo (try! (bar (try! (baz ()))))) (2x الأقواس!) ؛ عادة لا يمكن إعادة كتابة هذا كما حاول! (baz () ؟. bar () ؟. foo ()).

-
أنت تتلقى هذا لأنه تم ذكرك.
قم بالرد على هذه الرسالة الإلكترونية مباشرةً ، أو قم بعرضها على GitHub https://github.com/rust-lang/rust/issues/31436#issuecomment -244749275 ، أو تجاهل الموضوع https://github.com/notifications/unsubscribe-auth/ AARzN1Hdk6uk5-SoYawtgAbJUDf_8MsMks5qnBumgaJpZM4HUm_-.

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

https://github.com/colin-kiegel/rust-derive-builder/issues/25

شكرًا لأفكارك حول فكرة الماكرو nrc و withoutboats ، من الجيد سماع بعض الأسباب الملموسة لعدم

nielsle أنا لا أعتقد أن لها دقة أن نقول إن ? هو "أساسا" المستخدمة من قبل شركات البناء. في حين أن البناة هم مثال حيث أعتقد أن ميزة عامل postfix خفيف الوزن تتألق حقًا ، فأنا أفضل ? إلى try! في كل سياق.

nielsle بخصوص العقود الآجلة ، كنت مهتمًا في الأصل لأسباب مماثلة. ولكن ، بعد التفكير في الأمر ، أعتقد أن async / await سيحل محل أي حاجة لـ ? في هذا السياق. إنها في الواقع متعامدة إلى حد ما: يمكنك القيام بأشياء مثل (await future)?.bar() .

(ربما يكون من الجيد أن يكون لديك عامل لاحقة بدلاً من الكلمة الرئيسية await لذا فإن الأقواس ليست ضرورية. أو ربما تكون الأولوية المضبوطة بعناية كافية.)

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

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

solson أنت محق ، ربما لم يكن هذا هو المكان المناسب

أعتقد أن الجزء المهم من RFC هذا هو أن يكون لديك شيء من الجانب الأيمن من التعبير الذي يعمل مثل try! ، لأن هذا يجعل قراءة الاستخدامات المتسلسلة / المتسلسلة لـ "try" _ much_ أكثر قابلية للقراءة والحصول على "catch" . في الأصل ، كنت مؤيدًا بنسبة 100٪ لاستخدام ? كصيغة بناء ، لكنني عثرت مؤخرًا على بعض الرموز (النظيفة!) التي تستخدم بالفعل ? مما جعلني أدرك ذلك خارج الأمثلة البسيطة ? سهل للغاية تجاهله . وهو ما يجعلني الآن أعتقد أن استخدام ? لبناء جملة لـ "المحاولة الجديدة" قد يكون خطأً كبيرًا.

لذلك أقترح أنه قد يكون من الجيد _ إدخال بعض الاستطلاعات_ قبل الانتهاء منها (مع إخطار حول ذلك في المنتديات) للحصول على ملاحظات حول استخدام ? أو بعض الرموز الأخرى كنحو . على النحو الأمثل مع مثال للاستخدام في وظيفة أطول. لاحظ أنني أفكر فقط في أنه قد يكون من الجيد إعادة تسمية ? لعدم تغيير أي شيء آخر. يمكن أن يسرد الاستطلاع أسماء أخرى محتملة ظهرت في الماضي مثل ?! (أو ?? ) أو مجرد شيء مثل "use؟ vs use more than on character".

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

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

ملاحظة:
I طرح جوهر مع وظيفة يحب أعلاه في أشكال مختلفة ( "؟"، "؟!"، "؟؟") من خلال وأنا لا أعرف ما إذا كان هناك أكثر من ذلك. كما كان هناك RFC لإعادة تسمية ? إلى ?! والذي تمت إعادة توجيهه إلى هذه المناقشة.

عذرًا ، حول إمكانية إعادة تشغيل مناقشة جارية طويلة بالفعل: smiley_cat:.

(لاحظ أن ?? سيئ إذا كنت لا تزال ترغب في تقديم ? للخيار لأن expr??? سيكون غامضًا)

? من السهل جدًا التغاضي عنه

ماذا نناقش هنا؟ رمز غير محدد تمامًا؟ تصفح رمز مميز بشكل منتظم؟
أو تبحث بنشاط عن ? في دالة؟

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

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

dathinab أعتقد.

let namespace = namespace_opt.ok_or(Error::NoEntry).try!();

بهذه الطريقة يصعب تفويتها ، ولكن بنفس السهولة ، إن لم يكن من الأسهل الكتابة من .unwrap() .

eddyb : يتعلق الأمر ? والذي من شأنه أن يقدم مسار عودة آخر (/ path to catch) والتغيير المحتمل لنوع المتغير من Result<T> إلى T

CryZe : أتفق معك ولكن لا أعتقد أنه يمكننا الحصول على هذا في المستقبل القريب. وامتلاك شيء أقصر قليلاً من .try!() ليس سيئًا أيضًا.

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

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

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

CryZe : ربما يكون شيء مثل ?try نوعًا من الكلمات الرئيسية. لكن لأكون صادقًا ، لا أحب ذلك ( ?try ).

eddyb

وتعمل وظيفة البحث أيضًا

سيؤدي البحث عن ? إلى ظهور نتائج إيجابية خاطئة ، في حين أن الإصدارات الأطول لن تظهر على الأرجح.

البحث عن ؟ ستظهر نتائج إيجابية خاطئة ، بينما الإصدارات الأطول لن تظهر على الأرجح.

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

فمثلا،
screen-2016-09-15-175131

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

2016-09-15 23:52 GMT + 02: 00 ستيفن ألين [email protected] :

فمثلا،
[الصورة: screen-2016-09-15-175131]
https://cloud.githubusercontent.com/assets/310393/18568833/1deed796-7b6d-11e6-99af-75f0d7ddd778.png

-
أنت تتلقى هذا لأنه تم ذكرك.
قم بالرد على هذا البريد الإلكتروني مباشرة ، وقم بعرضه على GitHub
https://github.com/rust-lang/rust/issues/31436#issuecomment -247465972 ،
أو كتم الخيط
https://github.com/notifications/unsubscribe-auth/ABYmbjyrt07NXKMUdmlBfaciRZq7uBVEks5qqb4sgaJpZM4HUm_-
.

CryZe أنا متأكد من أن GitHub تستخدم فقط بعض أداة تمييز بناء الجملة مفتوحة المصدر ؛ يمكننا تصحيحه :-).

أعتقد بشكل عام أنه من الجيد أن يقدم مشروع Rust توصية مفادها أن أدوات تمييز بناء الجملة الخاصة بـ Rust يجب أن تستخدم أسلوبًا مرئيًا للغاية على ? ، لموازنة المخاوف بشأن الرؤية.

أعتقد أن ? هو أفضل خيار يمكننا القيام به هنا ، وكنا نستخدمه بكثافة في ميري .

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

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

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

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

اسمحوا لي أن أقوم بالمطاردة أولاً: قرر فريق @ rust-lang / lang تثبيت عامل التشغيل ? عند تطبيقه على قيم Result type. لاحظ أن ميزة catch لم يتم تثبيتها (وبالفعل لم يتم تنفيذها بعد) ؛ وبالمثل ، فإن ما يسمى ب "سمة الناقل" ، وهي وسيلة لتوسيع ? لأنواع مثل Option ، لا تزال في مرحلة المناقشة السابقة لـ RFC . ومع ذلك اتخذنا خطوات في تنفيذ الحالي لضمان أن نتمكن من إضافة Carrier سمة وقت لاحق (التي تعالج بعض مخاوفي السابقة حول تفاعل محتمل مع الاستدلال).

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

يجب أن يكون نطاق عامل التشغيل ? هو التعبير الحالي ، وليس الوظيفة الحالية.

عند حدوث خطأ ، ينشر الماكرو try! هذا الخطأ دون قيد أو شرط إلى وظيفة الاستدعاء (بمعنى آخر ، ينفذ return مع الخطأ). وفقًا للتصميم الحالي ، يتبع عامل التشغيل ? هذه السابقة ، ولكن بهدف دعم كلمة رئيسية catch تسمح للمستخدم بتحديد نطاق أكثر محدودية. هذا يعني ، على سبيل المثال ، أنه يمكن كتابة x.and_then(|b| foo(b)) كـ catch { foo(x?) } . في المقابل ، تستخدم العديد من اللغات الحديثة عامل التشغيل ? ليعني شيئًا مشابهًا أكثر لـ and_then ، وكان هناك قلق من أن هذا قد يكون مربكًا للمستخدمين الجدد.

في النهاية ، بمجرد تنفيذ catch ، فإن هذه مسألة افتراضات . وهناك العديد من الأسباب التي تجعلنا نعتقد أن الإعداد الافتراضي لـ "break out of function" (مع خيار التخصيص) هو أكثر ملاءمة لـ ? في Rust :

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

    • في المقابل ، فإن ? في Swift و Groovy وما إلى ذلك له علاقة بالقيم الفارغة أو أنواع الخيارات.

    • إذا اعتمدنا سمة الناقل ، فلا يزال من الممكن استخدام عامل التشغيل ? في Rust في السيناريوهات المذكورة (وبشكل خاص بالاقتران مع catch ) ، لكنها ليست حالة الاستخدام الرئيسية.

  • العديد من استخدامات and_then في Rust اليوم لن تعمل باستخدام طريقة الترميز ؛ قد يتم تضمين بعض الاستخدامات المستقبلية المتوقعة (مثل العقود الآجلة)

يحجب ? تدفق التحكم لأنه يصعب تحديده.

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

لماذا لا طريقة وحدات الماكرو؟

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

ما العقود التي يجب أن تعرض From ؟

في مناقشة RFC الأصلية ، قررنا تأجيل مسألة [ما إذا كان يجب أن تكون هناك "عقود" مقابل From ] ((https://github.com/rust-lang/rust/issues/31436#issuecomment -180558025). الإجماع العام لفريق lang هو أنه يجب على المرء أن يرى عامل التشغيل ? أنه يستدعي سمة From ، وأن From يمكنها القيام بأي شيء بشكل طبيعي مسموح به من خلال توقيعات النوع. لاحظ أن دور السمة From محدود تمامًا هنا على أي حال: يتم استخدامه ببساطة للتحويل من نوع واحد من الأخطاء إلى نوع آخر (ولكن دائمًا في سياق Result ).

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

محاولة ميناء! ليستخدم ؟

لا أعتقد أنه يمكننا القيام بذلك على الإطلاق بشكل عكسي ويجب أن نترك تنفيذ try! وحده. هل يجب علينا إيقاف العمل بـ try! ؟

nrc لماذا لا يتوافق مع الإصدارات السابقة؟

withoutboats try!(x) هو (x : Result<_, _>)? وربما يمكننا تنفيذه بهذه الطريقة إذا أردنا ذلك ، ولكن بشكل عام ، يمكن أن يستنتج x? أي شيء يدعم Carrier سمة (في المستقبل) ، أحد الأمثلة على ذلك هو iter.collect()? والذي بـ try! سيكون Result لكن يمكن أن يكون واقعيا Option .

منطقي. اعتقدت أننا قبلنا أن إضافة إشارات ضمنية إلى الأمراض المنقولة جنسياً قد تسبب غموضًا في الاستدلال ؛ لماذا لا تقبل هذا ايضا هنا؟

في كلتا الحالتين ، أعتقد أنه يجب إهمال try! .

؟ يكون أكثر فائدة في نمط منشئ مثل السياق ، بينما تعد try أكثر فائدة في طريقة متداخلة مثل السياق. أعتقد أنه لا ينبغي إهمالها ، مهما كان ذلك يعني.

@ est31 يفعلون الشيء نفسه بالضبط الآن باستثناء الاستدلال. لست متأكدًا مما تقصده بالضبط ، ولكن ? عادة ما يكون أكثر نظافة (استنتاج معياري مرة أخرى). هل يمكنك إعطاء أمثلة؟

eddyb foo()?.bar()?.baz() أجمل من try!(try!(foo()).bar()).baz() ، و try!(bar(try!(foo()))) أجمل من bar(foo()?)?

أجد ? أكثر قابلية للقراءة في كلتا الحالتين. يقلل من الفوضى غير الضرورية بين الأقواس.

متى ستصبح هذه الأرض مستقرة؟

ofek هذا https://github.com/rust-lang/rust/pull/36995 استقرت البنية الأساسية ? ، والتي يجب أن تكون مستقرة في 1.14.

لا تنس أن تضيف؟ عامل التشغيل إلى المرجع الآن بعد استقراره: https://doc.rust-lang.org/nightly/reference.html#unary -operator-expressions

وأشار bluss إلى أن الكتاب قديم أيضًا: https://doc.rust-lang.org/nightly/book/syntax-index.html

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

أنا أعارض التمديد؟ إلى "خيارات".

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

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

في 29 أكتوبر 2016 ، الساعة 11:08 -0400 ، كتب ticki [email protected] :

tomaka (https://github.com/tomaka)

أنا أعارض التمديد؟ إلى "خيارات".

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

-
أنت تتلقى هذا لأنه تم ذكرك.
قم بالرد على هذه الرسالة الإلكترونية مباشرةً ، أو قم بعرضها على GitHub (https://github.com/rust-lang/rust/issues/31436#issuecomment-257096575) ، أو تجاهل الموضوع (https://github.com/notifications/unsubscribe -auth / AABsipGIpTF1-7enk-z_5JRYYtl46FLPks5q42DCgaJpZM4HUm_-).

هذه (= ? نفسها) هي الآن ميزة مستقرة! (من الصدأ 1.13)

مشكلتان في التوثيق:

  • [x] تحديث فصل معالجة الأخطاء في الكتاب رقم 37750
  • [x] تحديث مستندات السمات Carrier للوضع الحالي # 37751

لاحظ أنه لم يتم تطبيق سمة catch ولا السمة Carrier بشكل صحيح حتى الآن ، فقط الميزة المجردة ? .

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

يجب على 35946 إزالة أي إشارة لـ Carrier من رسائل الخطأ. يبدو أنه يتعين علينا على الأقل إزالة الإشارة Option من مستندات Carrier .

أقوم بإضافة T-libs لهذه المشكلة بسبب التفاعل مع السمة Carrier

مرحبا؛ في # 31954 ، يتم تنفيذ upcast لنوع الخطأ باستخدام From (ومماثل في العنوان الحالي ) ، لكن RFC 243 ينص بوضوح على أنه يجب استخدام Into للتحويل.

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

(FWIW بصفتي مؤلف RFC 243 لم أفكر مليًا في ما إذا كان From أو Into هو الأفضل ، وربما اتخذ أو لم يتخذ القرار الصحيح. وهو فقط لأقول ذلك يجب تحديد السؤال بناءً على المزايا (والتي قد تتضمن في هذه المرحلة التوافق مع الإصدارات السابقة) ، بدلاً من ما هو مكتوب في RFC.)

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

راجع https://play.rust-lang.org/؟gist=6d3ee9f93c8b40094a80d3481b12dd00 ("مبسطة" من مشكلة العالم الحقيقي التي تتضمن fmt::Error في src / librustc / util / ppaux.rs # L81 )

هناك RFC جديد سيحل محل القديم من حيث وصف ? : rust-lang / rfcs / pull / 1859

أنا -1 على امتلاك كتل catch على الإطلاق ، عندما تكون أكثر إيجازًا بالفعل مع الإغلاق.

تتداخل عمليات الإغلاق مع بيانات التحكم في التدفق. ( break ، continue ، return )

الآن بعد أن تم دمج https://github.com/rust-lang/rfcs/pull/1859 ، وادعي أننا نعيد استخدام هذه المشكلة باعتبارها مشكلة تتبع ، أود أن أقترح إعادة تقييم catch جزء

لا أقترح التخلص منه تمامًا ، لكن الحماس مقابل catch لم يكن أبدًا كبيرًا كما كان لـ ? وأعتقد أنه يستحق التأكد من أنه لا يزال منطقيًا في الضوء من المصطلحات التي ظهرت مقابل ? ، والتي من المتوقع أن تظهر في ضوء Try .

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

تم تنفيذ الميزة ليلاً باستخدام الصيغة do catch ، أليس كذلك؟

withoutboats نعم ، إنه حاليًا do catch ، نظرًا لأن catch { ... } يتعارض مع القيم الحرفية ( struct catch { }; catch { } ).
.
archshift كما أشار SimonSapin أعلاه ، تتداخل عمليات الإغلاق مع break و continue و return .

bstrie أجد أنني كثيرًا ما أريد catch هذه الأيام ؛ يحدث كثيرًا أثناء إعادة البناء.

لم أكن أدرك أننا كنا نخطط لطلب do catch لبناء الجملة. بالنظر إلى أن خطر حدوث كسر في العالم الحقيقي يبدو منخفضًا للغاية (كلاهما ينتهك إرشادات تسمية البنية وسيتعين أن يكون المُنشئ هو التعبير الأول في البيان (وهو أمر نادر خارج موضع الإرجاع)) ، فربما يمكننا الاستفادة من rustfmt إعادة كتابة أي معرّفات مسيئة إلى catch_ ؟ إذا تطلب الأمر Rust 2.0 للقيام بذلك ، حسنًا ، لقد كنت دائمًا من يقول إن Rust 2.0 يجب أن يحتوي فقط على تغييرات تافهة على أي حال ...: P

bstrie لا نريد مطلقًا طلب do catch المدى الطويل. المناقشة التي أدت إلى استخدام بناء الجملة في الوقت الحالي هنا: https://github.com/rust-lang/rust/pull/39921

ممتاز ، شكرا على السياق.

لقد جئت للتو إلى هنا لأنني كنت آمل أن يكون catch مستقرًا ، وكان علي أن أتعلم أنه ليس كذلك - لذا نعم ، بالتأكيد ، الآن بعد أن أصبح ? مستقرًا ، سيكون من الرائع أيضًا أن يكون لديك catch .

أردت أن أرى إلى أي مدى وصلنا إلى ما تبقى من هذا ورأينا المناقشة.

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

يدعم FWIW ، C # في الواقع العكس: @ لاستخدام الكلمات الرئيسية كمعرفات. هذا شائع في Razor ، حيث تقوم بتمرير أشياء مثل new { <strong i="6">@class</strong> = "errorbox" } لتعيين الخصائص على عقد HTML.

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

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

أحد الاحتمالات الأخرى لمستقبل الكلمات الرئيسية: ضعها خلف سمة ، مثل نوع من الثبات #[feature] .

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

camlorn قد يثير اهتمامك هذا الموضوع ، وتحديداً فكرة آرون عن Rust "العصور": https://internals.rust-lang.org/t/pre-rfc-stable-features-for-breaking-changes/5002

أعتقد أنه يجب علينا ربط https://github.com/rust-lang/rust/issues/42327 من وصف المشكلة هنا. (ربما يجب أيضًا تحديث نص RFC للربط هناك بدلاً من هنا.)

(تحرير: لقد نشرت تعليقًا هناك ، لست متأكدًا من المشترك أو غير المشترك فيه بالفعل!)

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

لا أمانع إذا كانت بنية كتلة catch هي do { … } أو حتى ?{ … }

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

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

هذا الاقتراح ليس مناسبًا حقًا لـ Rust ، الذي يستخدم بالفعل الكتل العارية كتعبيرات.

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

لها خاصية مشكوك فيها (اعتمادًا على منظورك) لاستدعاء تدوين هاسكل الشبيه بالتدوين

rpjohnst Result و Option هما monads ، لذا فهي متشابهة من الناحية المفاهيمية على الأقل. يجب أيضًا أن يكون متوافقًا مع الأمام لتمديده في المستقبل لدعم جميع monads.

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

في الوقت نفسه ، بينما يمكننا على الأرجح جعله متوافقًا مع التدوين الكامل ، ربما لا ينبغي لنا إضافة تدوين كامل. لا يمكن تكوينه مع هياكل التحكم مثل if / while / for / loop أو break / continue / return ، والتي يجب أن نكون قادرين على استخدامها داخل وعبر الكتل catch (أو في هذه الحالة do ). (هذا لأنه يتم تعريف التدوين الكامل من حيث وظائف الترتيب الأعلى ، وإذا قمنا بحشو محتويات كتلة catch في سلسلة من عمليات الإغلاق المتداخلة ، فسوف تتحكم فجأة في التدفق في جميع الفواصل.)

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

nikomatsakis ، تم دمج # 42526 ، يمكنك وضع علامة "تم" عليه في قائمة التتبع :)

هل من الممكن جعل الكلمة الرئيسية catch سياقية مع تعطيلها إذا كان struct catch في النطاق ، وإصدار تحذير بالإيقاف؟

لست متأكدًا من مدى ملاءمة ذلك ، لكنني واجهت مشكلة ربما يحتاج هذا إلى حلها حيث أنك في بعض الأحيان تريد إجهاض قيمة جيدة بدلاً من قيمة الخطأ وهو أمر ممكن حاليًا عند استخدام return بقدر ما تريده غالبًا إحباط خطأ _inner_ من خلال _ router_ بخير. مثل عندما تقول دالة إرجاع Option<Result<_,_>> وهو أمر شائع جدًا من تكرار قول مكرر.

على وجه الخصوص لديّ ماكرو في مشروع أستخدمه بغزارة الآن:

macro-rules! option_try {
    ( $expr:expr ) => {
        match $expr {
            Ok(x)  => x,
            Err(e) => return Some(Err(e.into())),
        }
    }
}

من الشائع جدًا أن الوظيفة التي يتم استدعاؤها ضمن تنفيذ Iterator::next تحتاج إلى الإجهاض فورًا مع Some(Err(e)) عند الفشل. يعمل هذا الماكرو داخل جسم وظيفي عادي ولكن ليس داخل كتلة catch لأن المصيد لا يستحوذ على return قاطع ولكن فقط بناء الجملة الخاص ? .

على الرغم من أن المرتجعات الموصوفة في النهاية ستجعل فكرة الكتلة catch بأكملها زائدة عن الحاجة ، أليس كذلك؟

يبدو أن # 41414 قد انتهى. هل يمكن لشخص ما تحديث OP؟

التحديث: تم الآن دمج RFC rust-lang / rfcs # 2388 ولذا سيتم استبدال catch { .. } بـ try { .. } .
انظر قضية التتبع مباشرة فوق هذا التعليق.

ماذا يعني هذا بالنسبة لـ Edition 2015 ، هل لا تزال بنية do catch { .. } في طريقها نحو الاستقرار ، أم سيتم إسقاطها ودعمها فقط عبر try { .. } في الإصدار 2018+؟

@ Nemo157 الأخير.

هل هناك كشفان try ... catch ... في العرض الحالي؟ إذا كان الأمر كذلك ، فأنا لا أفهم الدلالات.

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

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

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

حسنًا ... يزعجني أن هذه الأسئلة لم يتم تسجيلها.

@ mark-im لذا للتوضيح ، أعتقد أنهم كانوا في مكان ما ؛ لكن ليس في مكان واحد ؛ إنه جهاز صراف آلي مبعثر قليلاً في العديد من RFCs والمشكلات وما إلى ذلك ، لذا ما نحتاج إليه هو تسجيلها في المواقع المناسبة.

يتم تتبع تصميم سمة الدعم في https://github.com/rust-lang/rust/issues/42327 ؛ هناك نقاش مكثف حول نقاط الضعف في الاتجاه الحالي والاتجاه الجديد المحتمل. (أخطط لإجراء طلب تقديمي مسبق لإجراء تغيير هناك بمجرد استقرار 2018 قليلاً).

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

سأضيف مربع اختيار لمهمة التنفيذ المعلقة التي أعرف أنه لا يزال يتعين القيام بها ...

scottmcm أعرف أن joshtriplett كان لديه مخاوف بشأن تغليف OK ( try RFC) وأود شخصياً تقييد break في التثبيت الأولي لـ try { .. } لذا أنه لا يمكنك فعل loop { try { break } } وما شابه.

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

بحيث لا يمكنك عمل loop { try { break } }

في الوقت الحالي ، لا يمكنك استخدام break في كتلة بدون حلقة ، وهذا صحيح: يجب استخدام break في الحلقات فقط. لترك كتلة try مبكرًا ، فإن الطريقة القياسية هي كتابة Err(e)? . ويفرض أن الأوراق المبكرة تكون دائمًا في مسار التحكم "غير الطبيعي".

لذا فإن اقتراحي هو أن الكود الذي أظهرته يجب أن يُسمح به ، ويجب أن يكسر loop ، وليس مجرد ترك try .

الفائدة الفورية ، عندما ترى break فأنت تعلم أنه سينقطع عن حلقة ، ويمكنك دائمًا استبدالها بـ continue . كما أنه يزيل الحاجة إلى تسمية نقطة الفاصل عند استخدام كتل try داخل loop وتريد الخروج من الحلقة.

Centril شكرا لك على رفع هؤلاء.

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

بالنسبة للتغليف Ok ، نعم ، أود معالجة ذلك قبل الاستقرار try .

centril نعم ، أنا على علم. ولكن من المهم أن نتذكر أن هذا هو إعادة إعادة إثارة القضية. قرر RFC تم أخذ الهدف الأصلي _ مرة أخرى _ ، وتغيير التنفيذ ليتبع RFC. لذا فإن سؤالي الكبير هو ما إذا كانت أي حقائق مادية قد تغيرت ، لا سيما بالنظر إلى أن هذا هو أحد أكثر الموضوعات صخبًا التي ناقشتها في RFCs + IRLO

scottmcm بالطبع ، كما تعلم ، أوافق على الاحتفاظ بـ Ok -wrapping ؛) وأنا أوافق على اعتبار المشكلة قد تمت تسويتها.

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

بشكل أساسي ، الموقف الذي أواجهه هو عمليات الاسترجاعات في إطار عمل واجهة المستخدم الرسومية - بدلاً من إرجاع Option أو Result ، يحتاجون إلى إرجاع UpdateScreen ، لإخبار الإطار إذا كانت الشاشة يحتاج إلى تحديث أم لا. غالبًا لا أحتاج إلى تسجيل الدخول على الإطلاق (من غير العملي ببساطة تسجيل الدخول لكل خطأ طفيف) وإرجاع UpdateScreen::DontRedraw عند حدوث خطأ. ومع ذلك ، مع عامل التشغيل الحالي ? ، يجب أن أكتب هذا طوال الوقت:

let thing = match fs::read(path) {
    Ok(o) => o,
    Err(_) => return UpdateScreen::DontRedraw,
};

نظرًا لأنني لا أستطيع التحويل من Result::Err إلى UpdateScreen::DontRedraw عبر عامل التشغيل Try ، فإن هذا يصبح مملاً للغاية - غالبًا ما يكون لدي عمليات بحث بسيطة في خرائط التجزئة التي يمكن أن تفشل (وهذا ليس خطأ ) - في كثير من الأحيان في رد اتصال واحد لدي من 5 إلى 10 استخدامات لعامل التشغيل ? . نظرًا لأن ما ورد أعلاه مطول جدًا للكتابة ، فإن الحل الحالي هو impl From<Result<T>> for UpdateScreen مثل هذا ، ثم استخدم دالة داخلية في رد الاتصال مثل هذا:

fn callback(data: &mut State) -> UpdateScreen {
     fn callback_inner(data: &mut State) -> Option<()> {
         let file_contents = fs::read_to_string(data.path).ok()?;
         data.loaded_file = Some(file_contents);
         Some(())
     }

    callback_inner(data).into()
}

نظرًا لاستخدام رد الاتصال كمؤشر وظيفي ، لا يمكنني استخدام -> impl Into<UpdateScreen> (لسبب ما ، لا يُسمح حاليًا بإرجاع impl لمؤشرات الوظيفة). لذا فإن الطريقة الوحيدة بالنسبة لي لاستخدام عامل التشغيل Try على الإطلاق هي القيام بخدعة الوظيفة الداخلية. سيكون من الرائع أن أفعل شيئًا كهذا:

impl<T> Try<Result<T>> for UpdateScreen {
    fn try(original: Result<T>) -> Try<T, UpdateScreen> {
        match original {
             Ok(o) => Try::DontReturn(o),
             Err(_) => Try::Return(UpdateScreen::DontRedraw),
        }
    }
}

fn callback(data: &mut State) -> UpdateScreen {
     // On any Result::Err, convert to an UpdateScreeen::DontRedraw and return
     let file_contents = fs::read_to_string(data.path)?;
     data.loaded_file = Some(file_contents);
     UpdateScreen::Redraw
}

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

تعديل:
لقد ارتكبت خطأ.


تجاهل هذا المنصب


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

fn test_try(a: u32, b: u32) {
    let div = if b != 0 {
        Some(a / b)
    } else {
        None
    };

    let x // : Option<_> // why is this type annotation necessary
    = try { div? + 1 };

    println!("{:?}", x);
}

إذا تمت إعادة كتابته لاستخدام الإغلاق بدلاً من كتلة المحاولة (وفي عملية التغليف التلقائي غير المحكم) ، فسنحصل على

fn test_closure(a: u32, b: u32) {
    let div = if b != 0 {
        Some(a / b)
    } else {
        None
    };

    let x =  (|| (div? + 1).into())();

    println!("{:?}", x);
}

وهو ما لا يتطلب نوع التعليق التوضيحي ، ولكنه يتطلب أن نلتف النتيجة.

ملعب

KrishnaSannasi يحتوي المثال الخاص بك القائم على الإغلاق على نوع فشل الاستدلال أيضًا ( الملعب ) لأن Into لا يقيد الإخراج ولا تستخدمه في أي مكان يحدث لاحقًا.

يبدو أن هذه مشكلة في الغالب مع السمة Try بدلاً من كتل try ، على غرار Into فهي لا تنشر أي نوع من المعلومات من المدخلات إلى المخرجات ، لذلك يجب تحديد نوع المخرجات باستخدامه لاحقًا. هناك _lot_ من المناقشة في https://github.com/rust-lang/rust/issues/42327 حول السمة ، لم أقرأها لذا لست متأكدًا مما إذا كان أي من المقترحات هناك يمكن أن يصلح هذه المشكلة.

@ Nemo157

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

إلى أي مدى نحن من تثبيت كتل المحاولة؟ إنها الميزة الوحيدة التي أحتاجها من كل ليلة: د

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

أعتقد أنه بمجرد القيام بذلك ، يمكن استقراره.

حاول block {} catch (أو معرّفات أخرى لاحقة) لترك مساحة التصميم مفتوحة للمستقبل ، وتوجيه الأشخاص إلى كيفية فعل ما يريدون بالمطابقة بدلاً من ذلك

ألا يوجد أي تصميم أساسي للسماح بالميزة الآن مع السماح بإمكانية ترك مساحة التصميم مفتوحة للمستقبل (وبالتالي كتلة catch نهاية المطاف)؟

يجب أن تحدد العلاقات العامة التي قمت بها هذا المربع على أي حال ، CCnikomatsakis

حاولت استخدام هذا لأول مرة بالأمس ، وفوجئت قليلاً أن هذا:

#![feature(try_blocks)]
fn main() -> Result<(), ()> {
    let x: () = try {
        Err(())?
    }?;
    Ok(x)
}

لا يتم تجميع بسبب

error[E0284]: type annotations required: cannot resolve `<_ as std::ops::Try>::Ok == _`

بدلا من ذلك ، كان علي أن أفعل

#![feature(try_blocks)]
fn main() -> Result<(), ()> {
    let x: Result<(), ()> = try {
        Err(())?
    };
    let x = x?;
    Ok(x)
}

في حين أن.

كان هذا محيرًا في البداية ، لذلك ربما يكون من المفيد تغيير رسالة الخطأ أو الإشارة في --explain ؟

إذا قمت بنقل علامة الاستفهام في المثال الأول الخاص بك إلى أسفل قليلاً

#![feature(try_blocks)]
fn main() -> Result<(), ()> {
    let x: () = try {
        Err(())?
    };
    Ok(x?)
}

تحصل على رسالة خطأ أفضل. يظهر الخطأ لأن Rust لا يمكنه تحديد النوع الذي يجب عليه حل الخطأ try { ... } to ، نظرًا لمدى عمومته. نظرًا لأنه لا يمكنه حل هذا النوع ، لا يمكنه معرفة نوع <_ as Try>::Ok ، وهذا هو سبب حصولك على الخطأ الذي فعلته. (لأن عامل التشغيل ? يزيل غلاف النوع Try ويعيد النوع Try::Ok ). لا يمكن أن يعمل الصدأ مع النوع Try::Ok بمفرده ، يجب حله من خلال السمة Try ، والنوع الذي ينفذ تلك السمة. (وهو تقييد للطريقة الحالية لأعمال التحقق من النوع)

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

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

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

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

أعتقد أن https://github.com/rust-lang/rfcs/pull/2388 قد استقر بشكل قاطع ما إذا كان try كاسم مقبول. هذا ليس سؤال مفتوح. ولكن يبدو أن تعريف السمة Try بالإضافة إلى تغليف Ok .

تم تحديد التفاف Ok بالفعل في RFC الأصلي ، ثم تمت إزالته أثناء التنفيذ ، وأخيرًا تمت إعادة إضافته لاحقًا. لا ارى كيف انه سؤال مفتوح.

rpjohnst حسنًا ، بسبب عدم موافقة جوش على القرار مع RFC الأصلي ... :) إنها مسألة محسومة بالنسبة لي . راجع https://github.com/rust-lang/rust/issues/31436#issuecomment -427096703 و https://github.com/rust-lang/rust/issues/31436#issuecomment-427252202 و https: // github.com/rust-lang/rust/issues/31436#issuecomment -437129491. على أي حال ... كان الهدف من تعليقي هو أن try كـ "لغة الاستثناءات" هي مسألة تمت تسويتها.

واو ، متى حدث هذا؟ كان آخر شيء أتذكره هو المناقشات حول الأجزاء الداخلية. أنا كثيرا ضد التفاف جيد جدا :(

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

Ok -wrapping كما هو مقبول في RFC 243 (حرفيا الذي حدد عامل التشغيل ? ، إذا كنت تتساءل متى حدث هذا) لا يغير أي شيء عن أنواع تعبيرات إرجاع الدوال. هذه هي الطريقة التي حددها RFC 243: https://github.com/rust-lang/rfcs/blob/master/text/0243-trait-based-exception-handling.md#catch -expressions

يقدم RFC هذا أيضًا تعبيرًا من catch {..} ، والذي يعمل على "نطاق" عامل التشغيل ? . المشغل catch ينفذ الكتلة المرتبطة به. إذا لم يتم طرح أي استثناء ، فستكون النتيجة Ok(v) حيث v هي قيمة الكتلة. خلاف ذلك ، إذا تم طرح استثناء ، فإن النتيجة هي Err(e) .

لاحظ أن catch { foo()? } يعادل في الأساس foo() .

أي أنها تأخذ كتلة من النوع T وتلفها دون قيد أو شرط لإنتاج قيمة من النوع Result<T, _> . أي عبارة return في الكتلة لا تتأثر تمامًا ؛ إذا كانت الكتلة هي التعبير الخلفي لوظيفة ما ، فيجب أن تُرجع الدالة Result<T, _> .

تم تنفيذه بهذه الطريقة ليلاً للأعمار: https://play.rust-lang.org/؟version=nightly&mode=debug&edition=2018&gist=88379a1607d952d4eae1d06394b50959. تم إجراء ذلك بعد نقاش كبير من قبل فريق lang في هذا الموضوع ، وربطه منه: rust-lang / rust # 41414 (وهذا مرتبط في الجزء العلوي من هذه المشكلة أيضًا).

في 28 مايو 2019 5:48:27 مساءً بتوقيت المحيط الهادي الصيفي ، كتب Alexander Regueiro [email protected] :

أوو. لا أصدق أن هذا حدث. Ok -التغليف فظيع جدا (هو
يكسر الحدس المعقول أن جميع التعبيرات تعود في ملف
يجب أن تكون الوظيفة من نوع إرجاع الوظيفة). لذا نعم ، بالتأكيد
مع @ mark-im على هذا. هل خلاف جوش كافٍ للحفاظ على هذا
قضية مفتوحة ، والحصول على مزيد من المناقشة حولها؟ كنت سأدعمه بكل سرور
في محاربة هذا ، لا يعني ذلك شيئًا بصفتك عضوًا من خارج الفريق.

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

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

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

في الثلاثاء ، 28 مايو ، 2019 الساعة 03:40:47 مساءً -0700 ، كتب راسل جونستون:

تم تحديد التفاف Ok بالفعل في RFC الأصلي ، ثم تمت إزالته أثناء التنفيذ ، وأخيرًا تمت إعادة إضافته لاحقًا. لا ارى كيف انه سؤال مفتوح.

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

في الثلاثاء ، 28 مايو ، 2019 الساعة 03:44:46 مساءً -0700 ، كتب مازداك فرخزاد:

على أي حال ... كان الهدف من تعليقي هو أن try كـ "لغة الاستثناءات" هي مسألة تمت تسويتها.

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

في الثلاثاء ، 28 مايو ، 2019 الساعة 11:37:33 مساءً -0700 ، كتب غابرييل سميث:

هل يمكن لأحدكم أن يشرح لماذا تجد غلاف Ok مقبول

كأحد الأسباب القليلة:

في الثلاثاء ، 28 مايو ، 2019 الساعة 05:48:27 مساءً -0700 ، كتب ألكسندر ريجويرو:

إنه يكسر الحدس المعقول للغاية بأن جميع تعبيرات الإرجاع في دالة يجب أن تكون من نوع إرجاع الوظيفة

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

لدي بالتأكيد أفكاري الخاصة هنا ، ولكن هل يمكننا من فضلك عدم إعادة فتح هذا الموضوع الآن؟ لقد أجرينا للتو أكثر من 500 محادثة مثيرة للجدل حول بناء الجملة ، لذلك أود تجنب الألغام الأرضية لفترة من الوقت.

إذا تم حظر هذا على فريق lang الذي يناقش ذلك ، فيجب إلغاء تحديد خانة الاختيار "حل ما إذا كان يجب" التفاف "قيمة النتيجة (# 41414)" مرة أخرى (ربما مع تعليق تم حظره في فريق lang) بحيث ينظر الأشخاص إلى هذه المشكلة تتبع تعرف الحالة؟

معذرةً ، لا أحاول إعادة فتح أي شيء - ما عليك سوى إعادة ذكر ما تم تحديده على أنه تم تحديده في مشكلة التتبع ومتى + كيف حدث ذلك.

@ rpjohnst شكرا للمعلومات!

yodaldevoid لخص جوش

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

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

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

هل تم الاتفاق على صياغة نوع التعليقات التوضيحية؟ كنت أتمنى الحصول على بعض try { foo()?; bar()?; }.with_context(|_| failure::err_msg("foon' n' barn'")?; ، وهو غير مهتم حتى عن بعد بالتجميع: error[E0282]: type annotations needed .

https://play.rust-lang.org/؟version=nightly&mode=debug&edition=2018&gist=4e60d44a8f960cf03307a809e1a3b5f2

لقد قرأت التعليقات منذ فترة وجيزة (وتحميل 300 تعليق مرة أخرى على جيثب أمر شاق للغاية) ، لكنني أتذكر أن معظم (إن لم يكن كل) الأمثلة المتعلقة بالمناقشة حول Try::Ok غلاف مستخدم Ok في المثال. بالنظر إلى أن Option ينفذ Try أيضًا ، أود أن أعرف كيف يؤثر ذلك على موقف الفريق في أي جانب من المناقشة يجب أن يكون.

في كل مرة أستخدم فيها Rust ، أظل أفكر "يا رجل ، أتمنى حقًا أن أتمكن من استخدام كتلة تجريبية هنا ،" ولكن حوالي 30٪ من الوقت هذا لأنني أتمنى حقًا أن أستخدم المحاولة مقابل Option s (مثل اعتدت على استخدام Scala ، والذي استخدم بناء الجملة for لتطبيقه على monads بشكل عام ، ولكنه يشبه إلى حد كبير try هنا).

اليوم فقط ، كنت أستخدم الصندوق json وهو يعرض طرق as_* التي تعيد الخيارات.

باستخدام الصيغتين ، سيكون المثال الخاص بي:

match s {
  "^=" => |a, b| try { a.as_str()?.starts_with(b.as_str()?) }.unwrap_or(false),
  "$=" => |a, b| try { Some(a.as_str()?.ends_with(b.as_str()?)) }.unwrap_or(false),
  // original
  "$=" => |a, b| {
    a.as_str()
      .and_then(|a| b.as_str().map(|b| (a, b)))
      .map(|(a, b)| a.starts_with(b))
      .unwrap_or(false)
    },
}

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

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

اعتقدت في الأصل أنه لن يكون التفاف Ok أفضل في الحالة التي تكون فيها العبارة الأخيرة عبارة عن دالة تُرجع النوع الذي ينفذ Try ، لكن الاختلاف في بناء الجملة سيكون

try {
  fallible_fn()
}

try {
  fallible_fn()?
}

وفي هذه الحالة ، أعتقد مرة أخرى أن التغليف أفضل من Ok لأنه يوضح أن fallible_fn هي دالة إرجاع Try ، لذا فهي في الواقع أكثر وضوحًا.

أريد أن أعرف ما هو رأي المعارضة في هذا ، وبما أنني لا أستطيع رؤية العديد من الآخرين في هذا الموضوع ، @ joshtriplett.

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

كنت أرغب في إعطاء try لقطة لبعض تحليل Option المتداخل أيضًا:

#![feature(try_blocks)]

struct Config {
    log: Option<LogConfig>,
}

struct LogConfig {
    level: Option<String>,
}

fn example(config: &Config) {
    let x: &str = try { config.log?.level? }.unwrap_or("foo");
}

هذا فشل مع

error[E0282]: type annotations needed
  --> src/lib.rs:12:19
   |
12 |     let x: &str = try { config.log?.level? }.unwrap_or("foo");
   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type
   |
   = note: type must be known at this point

أقرب ما حصلت عليه كان

fn example(config: &Config) {
    let x: Option<&str> = try { &**config.log.as_ref()?.level.as_ref()? };
    let x = x.unwrap_or("foo");
}

as_ref أمر مؤسف للغاية. أعلم أن Option::deref سيساعد البعض هنا ، لكن ليس كافيًا. هذا يبدو وكأنه يتطابق بطريقة أو بأخرى مع بيئة العمل (أو فكرة ذات صلة) يجب أن تلعب دورًا.

الأسطر المتعددة مؤسفة أيضًا.

هل يمكن أن يستخدم try استنتاجًا احتياطيًا Result مثل القيم الحرفية الصحيحة؟ هل سيسمح ذلك لمحاولةshepmaster الأولى باستنتاج Result<&str, NoneError> ؟ ما هي المشكلات المتبقية - ربما العثور على نوع خطأ شائع لـ ? s للتحويل إليه؟ (هل فاتني مناقشة هذا في مكان ما؟)

shepmaster أتفق مع نوع الاستدلال. من المضحك أنه على الرغم من ذلك ، فقد جربت الكود الدقيق الخاص بك باستخدام تطبيق بسيط إلى حد ما try_ وهو يعمل بشكل جيد: https://github.com/norcalli/koption_macros/blob/4362fba8fa9b6c62fdaef4df30060234381141e7/src/lib.rs#L23

    let x = try_! { config.log?.level? }.unwrap_or("foo".to_owned());
    assert_eq!(x, "debug");

يعمل بشكل جيد.

تنفيذ try_ ساذج إلى حد ما

نعم ، لكن استدعاء الماكرو الخاص بك يُرجع String ، وليس &str ، يتطلب الملكية. أنت لا تعرض الكود المحيط ، لكن هذا سيفشل لأننا لا نملك ملكية Config :

fn example(config: &Config) {
    let x = try_! { config.log?.level? }.unwrap_or_else(|| String::from("foo"));
}
error[E0507]: cannot move out of captured variable in an `Fn` closure
  --> src/lib.rs:20:21
   |
19 | fn example(config: &Config) {
   |            ------ captured outer variable
20 |     let x = try_! { config.log?.level? }.unwrap_or_else(|| String::from("foo"));
   |                     ^^^^^^^^^^ cannot move out of captured variable in an `Fn` closure

كما أنه يخصص دون قيد أو شرط String ؛ لقد استخدمت unwrap_or_else في هذا المثال لتجنب عدم الكفاءة.

إنه لأمر مخز أن هذه الميزة لم يتم تثبيتها قبل كتل async/await . IMO كان من الممكن أن يكون أكثر اتساقًا

let fut = async try {
    fut1().await?;
    fut2().await?;
    Ok(())
};

بدلاً من السماح لها بالعمل بدون try . لكنني أفترض أن تلك السفينة أبحرت منذ فترة طويلة.

إعادة: التفاف تلقائي ، لا أعتقد أنه سيكون من الممكن أن يكون متسقًا مع كتل async الآن. تقوم كتل async "التفاف تلقائي" من نوع ما ، إلى نوع مجهول يقوم بتنفيذ Future . ولكن هذا صحيح حتى مع العوائد المبكرة ، والتي لن تكون ممكنة مع كتل try .

قد يصبح مربكًا بشكل مضاعف إذا كان لدينا كتلة افتراضية async try . هل يجب أن يتم تغليف النتيجة تلقائيًا؟

لا أعتقد أننا فقدنا أي فرص في الاتساق بمعنى التغليف التلقائي. كلا async الكتل وظائف يمكن استخدامها ? ، وكلاهما يجب أن نفعل اليدوية الخاصة Ok -wrapping، من العائدات المبكرة والنهائية.

من ناحية أخرى ، يمكن أن تستخدم كتلة try ? مع التفاف تلقائي Ok ، بما في بافتراض ميزة إرجاع مبكرة - ربما علامة - فاصل- قيمة . يمكن لوظيفة المحاولة الافتراضية أن تقوم بسهولة بالتغليف التلقائي Ok على كل من المرتجعات المبكرة والنهائية.

يمكن للكتلة الافتراضية async try أن تدمج ببساطة وظائف التفاف تلقائي - Ok التفاف ، ثم auto- Future -wrap. (الطريقة الأخرى مستحيلة التنفيذ ، ويمكن القول إن كتابتها try async أي حال.)

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

هل هناك أي شيء يوقف استقرار هذا الأمر ، أو لم يأخذ أحد الوقت الكافي للقيام بذلك حتى الآن؟ أنا مهتم بإنشاء العلاقات العامة الضرورية وإلا 🙂

في 18 تشرين الثاني (نوفمبر) 2019 ، الساعة 2:03:36 صباحًا بتوقيت المحيط الهادي ، كتب Kampfkarren [email protected] :

أي شيء يوقف استقرار هذا ، أو لم يأخذ أحد
حان الوقت للقيام بذلك؟ أنا مهتم بإنشاء العلاقات العامة الضرورية
خلاف ذلك 🙂>
>
->
أنت تتلقى هذا لأنه تم ذكرك.>
قم بالرد على هذه الرسالة الإلكترونية مباشرة أو اعرضها على GitHub:>
https://github.com/rust-lang/rust/issues/31436#issuecomment -554944079

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

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

أستطيع أن أتخيل حالات صعبة مثل غموض Result<Result<T,E>> ، لكن في مثل هذه الحالات الغامضة ، يمكننا العودة إلى عدم الالتفاف. يمكن للمستخدم بعد ذلك أن يلتف بشكل صريح لإزالة الغموض. هذا لا يبدو سيئًا للغاية ، لأنني لا أتوقع أن يظهر هذا النوع من الغموض كثيرًا ...

يجب ألا يكون هناك أي غموض على الإطلاق ، لأنه ليس غلافًا إجباريًا Ok Ok ولكنه غلاف Ok . سينتج try { ...; x } Ok(x) تمامًا مثل Ok({ ...; x }) .

@ joshtriplett هل هذا لم resolve whether catch blocks should "wrap" result value تم تحديدها ، نقلاً عن https://github.com/rust-lang/rust/issues/41414

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

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

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

@ mark-im كيف ترى بالضبط إضافة غلاف جيد في المستقبل؟ أحاول معرفة كيف يمكن القيام بذلك ، ولا يمكنني رؤيته تمامًا.

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

سنقوم بتثبيت كتلة try بدون تغليف جيد. فمثلا:

let x: Result<usize, E> = try { 3 }; // Error: expected Result, found usize
let x: Result<usize, E> = try { Ok(3) }; // Ok (no pun intended)

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

let x: Result<usize, E> = try { 3 }; // Ok
let x: Result<usize, E> = try { Ok(3) }; // Also Ok for backwards compat
let x: Result<Result<usize, E1>, E2> = try { Ok(3) }; // Ok(Ok(3))
let x: Result<Result<usize, E1>, E2> = try { Ok(Ok(3)) }; // Ok(Ok(3))

السؤال هو ما إذا كان هذا يمكن أن يتسبب في جعل شيء ما غامضًا لم يكن من قبل. فمثلا:

let x = try { Err(3) }; // If x: Result<Result<T1, usize>, usize>, then it is not clear if user meant Ok(Err(3)) or Err(3)...

على الرغم من ذلك ، ربما يجب أن يتعامل غلاف جيد بالفعل مع هذه المشكلة؟

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

ماذا عن استخدام تغليف جيد باستثناء أن النوع الذي تم إرجاعه هو Result أو Option ؟ سيسمح ذلك برمز أبسط في معظم الحالات ولكنه سيسمح بتحديد القيمة الدقيقة عند الحاجة.

// Ok-wrapped
let v: Result<i32, _> = try { 1 };

// not Ok-wrapped since the returned type is Result
let v: Result<i32, _> = try { Ok(1) };

// not Ok-wrapped since the returned type is Result
let v: Result<i32, _> = try { Err("error") };

// Ok-wrapped
let v: Option<i32> = try { 1 };

// not Ok-wrapped since the returned type is Option
let v: Option<i32> = try { Some(1) };

// not Ok-wrapped since the returned type is Option
let v: Option<i32> = try { None };

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

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

let x = try { Err(3) }; // If x: Result<Result<T1, usize>, usize>, then it is not clear if user meant Ok(Err(3)) or Err(3)...

على الرغم من ذلك ، ربما يجب أن يتعامل غلاف جيد بالفعل مع هذه المشكلة؟

لا ، هذا لا لبس فيه Ok(Err(3)) ، Ok التفاف مستقل عن بناء الجملة أو الأنواع ، فهو يلتف فقط مهما كان ناتج الكتلة في المتغير Try::Ok .

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

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

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

let c = try { 2 * a? + b? };

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

نظرًا للتاريخ الموقر لمشكلة التتبع هذه ، ودمجها الأصلي والمؤسف مع المشغل ? ، والحاجز على مشكلة التفاف Ok ، أقترح إغلاق هذه المشكلة على الفور وإرسال try بالعودة إلى بداية عملية RFC ، حيث يمكن لهذه المناقشة الحصول على الرؤية التي تستحقها و (نأمل) الوصول إلى نوع من الاستنتاج.

بدون غلاف جيد ، سيكون هذا أقل راحة

هل يمكنك أن تشرح بالتفصيل الأشياء غير المريحة بالضبط التي ستقدمها؟

بدون تغليف جيد ، سيبدو مثالك كما يلي:

let c = try { Ok(2 * a? + b?) };

وهو أمر جيد في رأيي.

أعني ، مع مثال صغير مثل هذا ، قد يبدو الأمر مبالغة ولكن كلما زاد عدد الكود الذي يحتوي على كتلة try كان التأثير الأقل الذي يسببه غلاف Ok(...) .

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

هذا الماكرو غير ممكن بينما السمة Try غير مستقرة.

لماذا ا؟ على أي حال ، عندما يستقر (افتراضيًا ليس بعيدًا جدًا في المستقبل) ، سيكون ذلك ممكنًا جدًا.

@ Nemo157 try أيضًا في الليل فقط في الوقت الحالي ، ومن المحتمل ألا تستقر في الحالة غير المرجحة التي قررنا فيها اقتلاع try Try . هذا يعني أنه من المحتمل ألا يستقر قبل Try . لذا ، فإن القول بأن الماكرو غير ممكن لا معنى له.

KrishnaSannasi لدي فضول لماذا قد يتم Try ؟

@ mark-im لا أعتقد أنه سيكون كذلك ، أنا فقط أشرح لماذا القلق بشأن Try التواجد ليلاً لتجربة الكتل ليس مصدر قلق واقعي. إنني أتطلع إلى Try على الاستقرار.

بالنظر إلى أن ? قد استقر بالفعل ، وأن الكتل try لها تصميم واضح يشمل كلاً من Result و Option بنفس الطريقة التي بها ? لا، ليس هناك سبب أستطيع أن أرى لمنع استقرار لهم على استقرار Try . لم أراقب ذلك عن كثب ، لكن انطباعاتي كانت أن هناك إجماعًا أقل بكثير على تصميم Try مقارنةً بالكتل try ، لذلك يمكنني رؤية try كتل الاستقرار قبل سنوات Try (كما حدث لـ ? ). وحتى إذا تم التخلي عن السمة Try لا أرى أي سبب يمنع استقرار كتل try حيث تعمل مع Result و Option مثل ? سيكون

(من أجل _لماذا_ لا يمكنك كتابة هذا الماكرو نظرًا للكتل المستقرة Try وخاصية try غير المستقرة ، سيتوسع الماكرو إلى try { Try::from_ok($expr) } ؛ يمكنك إنشاء وحدات ماكرو لكل نوع مقابل Result و Option ، لكن IMO لن تلبي نقطة "من السهل جدًا [...] محاكاة").

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

أود أن أقترح المفهوم التالي لمحاولة التقاط الدلالة ...

ضع في اعتبارك الكود التالي:

union SomeFunctionMultipleError {
    err0: Error1,
    err1: Error2,
}

struct SomeFunctionFnError {
    index: u32,
    errors: SomeFunctionMultipleError,
}

fn some_function() -> Result<i32, SomeFunctionFnError> {
    if 0 == 0 {
        Ok(2)
    } else {
        Err(SomeFunctionFnError{ index: 0, errors: SomeFunctionMultipleError {err1: Error2 {id0: 0, id1: 0, id3: 0}}})
    }
}

union OtherFunctionMultipleError {
    err0: Error1,
    err1: Error2,
    err2: Error3,
}

struct OtherFunctionFnError {
    id: u32,
    errors: OtherFunctionMultipleError,
}

fn other_function() -> Result<i32, OtherFunctionFnError> {
    if 0 == 0 {
        Ok(2)
    } else {
        Err(OtherFunctionFnError {id: 0, errors: OtherFunctionMultipleError {err0: Error1 {id0: 0, id1: 0}}})
    }
}

هذا هو الكود الذي يمكن إنشاؤه بواسطة استثناءات Zero-Overhead في Rust باستخدام ميزة بناء الجملة التالية:

fn some_function() -> i32 throws Error1, Error2 {
    if 0 == 0 {
        2
    } else {
        Error2 {id0: 0, id1: 0, id3: 0}.throw
    }
}

fn other_function() -> i32 throws Error1, Error2, Error3 {
    if 0 == 0 {
        2
    } else {
        Error1{id0: 0, id1: 0}.throw
    }
}

أو حتى هذه الأخطاء يمكن أن يستنتجها المترجم ضمنيًا:

fn some_function(i: i32) -> i32 throws { // Implicitly throws Error1, Error2
    if i == 0 {
        2
    } else if i == 1 {
        Error1 {id0: 0, id1: 0, id3: 0}.throw
    } else {
        Error2 {id0: 0, id1: 0, id3: 0}.throw
    }
}

fn other_function(i: i32) -> i32 throws { // Implicitly throws Error1
    if i == 0 {
        2
    } else {
        Error1{id0: 0, id1: 0}.throw
    }
}

هذا السكر النحوي لا شيء آخر !! السلوك هو نفسه !!

تحية للجميع،

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

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

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

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

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

أود أن أذكر أنه في التطبيق الحالي ، لا تعد كتلة try تعبيراً. أعتقد أن هذا سهو.

هل يمكنك نشر الرمز الذي لا يعمل من أجلك؟ يمكنني استخدامه في سياق تعبير هنا: ( ملعب الصدأ )

#![feature(try_blocks)]

fn main() {
    let s: Result<(), ()> = try { () };
}

بالتأكيد ، ها هو .

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

@ Nokel81 المشكلة في المثال السابق هي أن التعبير الموجود في if let $pat = $expr ليس سياق تعبير عادي ، ولكنه سياق خاص "بدون تعبير بين قوسين". للحصول على مثال حول كيفية عمل ذلك مع تعبيرات البنية ، راجع هذا المثال حيث يكون من الواضح من الناحية التركيبية وجود تعبير هيكلي هناك ، وهذا المثال ليس كذلك. لذا فإن الخطأ ليس أن try ليس تعبيرًا ، ولكن الخطأ خاطئ ، ويجب أن يقول "تعبير try غير مسموح به هنا ؛ حاول إحاطة هذا التعبير بأقواس" (و تحذير غير صحيح حول الأقواس غير الضرورية تم منعها).

المثال الأخير الخاص بك هو في الواقع غامض لاستدلال النوع. نوع e هو _: From<usize> في هذه الحالة ، وهي معلومات غير كافية لمنحه نوعًا ملموسًا. ستحتاج إلى استخدامه بطريقة ما لمنحه نوعًا ملموسًا للسماح باستدلال النوع بالنجاح. هذه ليست مشكلة خاصة بـ try ؛ إنها طريقة عمل الاستدلال الكتابي في Rust.

الآن ، إذا حاولت على الفور المطابقة كحالة Ok وتجاهلت حالة Err ، فلديك حالة لرسالة خطأ دون المستوى الأمثل مع عدم وجود طريقة بسيطة حقيقية

آه ، شكرًا جزيلاً لك على الشرح المتعمق. أعتقد أنني ما زلت في حيرة من أمري لماذا لاحقًا غامضة لاستدلال النوع. لماذا نوع التعبير ليس Result<isize, usize> ؟

يمكن للعامل ? إجراء تحويلات النوع بين أنواع الأخطاء المختلفة باستخدام سمة From . يتوسع تقريبًا إلى رمز الصدأ التالي (تجاهل سمة Try ):

match expr {
    Ok(v) => v,
    Err(e) => return From::from(e),
}

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

أعتذر إذا تم معالجة هذا بالفعل ، لكن يبدو لي أنه من الغريب أن:

#![feature(try_blocks)]

fn main() -> Result<(), ()> {
    let result = try { // no type annotation
        Err(())?;
    };
    result.map_err(|err| err)
}

فشل في التحويل البرمجي مع:

error[E0282]: type annotations needed

لكن:

#![feature(try_blocks)]

fn main() -> Result<(), ()> {
    let result : Result<_, _> = try { // partial type annotation
        Err(())?;
    };
    result.map_err(|err| err)
}

على ما يرام.

إذا كانت هذه مشكلة لأن وسيطات النوع Result لا يمكن استنتاجها ، فسأفهم ، ولكن ، كما هو موضح أعلاه ، ليس هذا هو الحال ، ويمكن لـ rustc إجراء الاستدلال بمجرد إخباره بأن نتيجة تعبير try هو نوع من Result ، والذي يجب أن يكون قادرًا على الاستنتاج من core::ops::Try::into_result .

أفكار؟

nwsharp هذا لأن try / ? عام أكثر من Try الأنواع. إذا كان لديك نوع آخر كان impl Try<Ok=_, Error=()> ، فيمكن تقييم كتلة المحاولة لهذا النوع بالإضافة إلى Result . Desugared ، مثالك تقريبًا

#![feature(try_trait, label_break_value)]

use std::ops::Try;

fn main() -> Result<(), ()> {
    let result /*: Result<_, _>*/ = 'block: {
        match Try::into_result(Err(())) {
            Ok(ok) => Try::from_ok(ok),
            Err(err) => {
                break 'block Try::from_error(From::from(err));
            }
        }
    };
    result.map_err(|err| err)
}

@ CAD97 شكرا لك على الشرح.

ومع ذلك ، لم أكن أتوقع أن يكون try قادرًا بشكل فعال على إحداث نوع من التحويل بين أدوات Try مختلفة.

أتوقع حدوث انخفاض في الارتفاع حيث يتم تحديد نفس الأداة Try لـ into_result ، from_ok ، و from_error .

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

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

try { ... }.into()

مع ضمانة البطانية المقابلة:

impl<T: Try, E: Into<T::Err>> From<Result<T::Ok, E>> for T {
    fn from(result: Result<T::Ok, E>) -> Self {
        match result {
            Ok(ok) => T::from_ok(ok),
            Err(err) => T::from_err(err.into()),
        }
    }
}

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

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

أو الذهاب أبعد من ذلك مع ضمنية البطانية ، أفترض.

impl <T: Try, U: Try> From<U> for T 
    where U::Ok : Into<T::Ok>, U::Err : Into<T::Err>
{
    fn from(other: U) -> Self {
        match other.into_result() {
            Ok(ok) => Self::from_ok(ok.into()),
            Err(err) => Self::from_err(err.into()),
        }
    }
}

أو أيا كان...

ومع ذلك ، لم أكن أتوقع أن تكون المحاولة فعالة قادرة على إحداث نوع من التحويل بين أدوات Try مختلفة.

أتوقع حدوث انخفاض في الارتفاع حيث يتم تحديد نفس الأداة Try لـ into_result ، from_ok ، و from_error .

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

هناك أربعة أنواع ثابتة من Try : Option<T> و Result<T, E> و Poll<Result<T, E>> و Poll<Option<Result<T, E>> .

NoneError غير مستقر ، لذا فإن Option<T> عالق في المحاولة في Option<T> بينما NoneError غير مستقر. (لاحظ أن المستندات تستدعي صراحة From<NoneError> كـ "تمكين option? لنوع الخطأ الخاص بك.")

ومع ذلك ، يشير Poll تعيين نوع الخطأ إلى E . لهذا السبب ، فإن "نوع التحويل" لـ Try مستقر ، لأنه يمكنك الحصول على ? a Poll<Result<T, E>> في -> Result<_, E> للحصول على Poll<T> وإرجاع حالة E مبكرًا.

في الواقع ، هذا يمنح المساعد الصغير "اللطيف" :

fn lift_err<T, E>(x: Poll<Result<T, E>>) -> Result<Poll<T>, E> { Ok(x?) }

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

هل تم التفكير في السماح بمواصفات impl Try المطلوبة لتهدئة السلوك غير البديهي هنا؟

على سبيل المثال ، زواج الدرّاجات قليلاً ، try<T> { ... } . أم أن هناك شيئًا آخر يمكن التعثر فيه حتى مع ذلك؟

ربما لإضافة لون أكثر قليلاً هنا ، حقيقة أن try { } على مجموعة من Result لا ينتج "فقط" Result أمر غير متوقع ويحزنني . أنا أفهم السبب ، لكني لا أحب ذلك.

نعم ، كان هناك نقاش حول الجمع بين "النوع العام ascription" (هناك مصطلح تبحث عنه) و try . أعتقد ، آخر ما سمعته ، كان من المقرر أن يعمل try: Result<_, _> { .. } في النهاية

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

يرجى الاطلاع على هذه المشكلة المنفصلة لسؤال ضيق محدد لحل إجماع فريق اللغة حول مسألة التفاف Ok .

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

لا أرى سبب الحاجة إلى كتلة try . هذا النحو

fn main() -> Result<(), ()> {
    try {
        if foo() {
            Err(())?
        }
        ()
    }
}

يمكن استبداله بهذا:

fn main() -> Result<(), ()> {
    Ok({
        if foo() {
            Err(())?
        }
        ()
    })
}

كلاهما يستخدم نفس عدد الأحرف ، لكن الثاني ثابت بالفعل.

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

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

`` صدأ
fn main () / * لا توجد نتيجة هنا * / {
دع النتيجة = جرب {
foo () ؟. bar () ؟. baz ()؟
} ؛
نتيجة المباراة {
//…
}
}

SimonSapin هل يأتي ذلك كثيرًا؟ نادرًا ما أواجه موقفًا يكون منطقيًا ، وعادة ما تكون هناك طريقة جيدة للالتفاف عليه. في مثالك:

fn main() /* no result here */ {
    let result  = foo()
        .and_then(|x| x.bar())
        .and_then(|x| x.baz());
    match result {
        // …
    }
}

هذا مطول أكثر ، لكنني أعتقد أن الحل الأبسط سيكون بناء جملة لإغلاق الطريقة:

fn main() /* no result here */ {
    let result  = foo()
        .and_then(::bar)
        .and_then(::baz);
    match result {
        // …
    }
}

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

يحتوي RFC المقبول على بعض الأسباب الأخرى: https://rust-lang.github.io/rfcs/0243-trait-based-exception-handling.html

على أي حال ، تمت مناقشة الحجج المؤيدة لتركيبات اللغة لتدفق التحكم باستخدام عامل التشغيل ?.await ) على طرق التسلسل مثل and_then باستفاضة.

على أي حال ، تمت مناقشة الحجج المؤيدة لتركيبات اللغة لتدفق التحكم باستخدام عامل التشغيل ?.await ) على طرق التسلسل مثل and_then باستفاضة.

SimonSapin شكرا. أقنعني هذا وإعادة قراءة RFC أن هذا يمكن أن يكون مفيدًا.

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

لقد كتبت وظيفة صغيرة تعمل بشكل جيد. لاحظ أن File::open()? فشل مع std::io::Error بينما فشل السطر التالي مع anyhow::Error . على الرغم من اختلاف الأنواع ، يكتشف المترجم كيفية تحويل كليهما إلى Result<_, anyhow::Error>

fn tls_add_cert(config: &ClientConfig, path: impl AsRef<Path>) -> Result<(usize, usize), anyhow::Error> {
    let path = path.as_ref();
    let mut file = BufReader::new(File::open(path)?);
    Ok(config.root_store.add_pem_file(&mut file)
        .map_err(|_| anyhow!("Bad PEM file"))?)
}

أردت إضافة بعض سياق الخطأ ، لذلك حاولت استخدام كتلة try-block وعلى أي حال with_context() :

fn tls_add_cert(config: &ClientConfig, path: impl AsRef<Path>) -> anyhow::Result<(usize, usize)> {
    let path = path.as_ref();
    try {
        let mut file = BufReader::new(File::open(path)?);
        Ok(config.root_store.add_pem_file(&mut file)
            .map_err(|_| anyhow!("Bad PEM file"))?)
    }
    .with_context(|| format!("Error adding certificate {}", path.display()))
}

ولكن الآن اكتب فشل الاستدلال:

error[E0282]: type annotations needed
  --> src/net.rs:29:5
   |
29 | /     try {
30 | |         let mut file = BufReader::new(File::open(path)?);
31 | |         Ok(config.root_store.add_pem_file(&mut file)
32 | |             .map_err(|_| anyhow!("Bad PEM file"))?)
33 | |     }
   | |_____^ cannot infer type
   |
   = note: type must be known at this point
   ```

I don't understand why a type annotation is needed here but not in the first case. Nor do I see any easy way to add one, as opposed to using an [IIFE](https://en.wikipedia.org/wiki/Immediately_invoked_function_expression) which does let me add an annotation:

```rust
(|| -> Result<_, anyhow::Error> {
    let domain = DNSNameRef::try_from_ascii_str(host)?;
    let tcp = TcpStream::connect(&(host, port)).await?;

    Ok(tls.connect(domain, tcp).await?)
})()
.with_context(|| format!("Error connecting to {}:{}", host, port))

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

مرة أخرى،

هذا لأن try / ? عام أكثر من Try الأنواع. إذا كان لديك نوع آخر كان [ impl Try<Ok=_, Error=anyhow::Error> ] ، يمكن تقييم كتلة المحاولة لهذا النوع بالإضافة إلى Result .

(أيضا، لا تحتاج إلى Ok التعبير زائدة الخاص في try كتلة (# 70941)).

أعتقد أن استمرار ظهور هذا يعني ذلك

  • قبل التثبيت ، يحتاج try إلى دعم نوع إسناد ( try: Result<_,_> { أو أيًا كان) أو التخفيف من هذه المشكلة بطريقة أخرى ،
  • يحتاج هذا بالتأكيد إلى تشخيصات مستهدفة عندما يفشل استنتاج نوع try block ، و
  • يجب أن نفكر بشدة في إعطاء try نوعًا احتياطيًا إلى Result<_,_> عندما لا يكون مقيدًا بطريقة أخرى. نعم ، هذا أمر صعب ، وغير محدد بشكل كافٍ ، ومن المحتمل أن يكون إشكاليًا ، ولكنه _ سيحل_ 80٪ من الكتل try تحتاج إلى تعليق توضيحي لأن $12: Try<Ok=$5, Error=$8> ليس محددًا بدرجة كافية.

بالإضافة إلى ذلك ، نظرًا لأن # 70941 يبدو أنه يتجه نحو "نعم ، نريد (شكلًا ما)" تغليف Try::from_ok "" ، ربما _ أيضًا_ نريد تشخيصًا مستهدفًا عندما يكون التعبير الذيل try block بإرجاع Ok(x) عندما يعمل x .

أظن أن السلوك الصحيح للمحاولة هو

  • تمديد بناء الجملة للسماح بنسب يدوي مثل try: Result<_, _> { .. } أو try as Result<> أو أيًا كان (أعتقد أن try: Result ربما يكون جيدًا؟ يبدو أنه الصيغة المفضلة)
  • افحص "النوع المتوقع" الذي يأتي من السياق - إذا كان موجودًا ، يفضل أن يكون نوع النتيجة try
  • خلاف ذلك ، افتراضيًا إلى Result<_, _> - هذا ليس نوع الاستدلال الاحتياطي كما هو الحال مع i32 ، فقد يحدث في وقت سابق ، ولكن هذا يعني أن أشياء مثل try { }.with_context(...) تجميع.

ومع ذلك ، أشعر بالقلق من احتمال ظهور أخطاء حول ? into والإكراه ? نتيجة كتلة try ، مثل:

#![feature(try_blocks)]

use std::error::Error;
fn foo() -> Result<(), Box<dyn Error>> {
    let x: Result<_, _> = try {
        std::fs::File::open("foo")?;
    };

    x?;

    Ok(())
}

fn main() { 
}

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

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

ربما يتضمن نوعًا من الاستدلال الاحتياطي الذي سيجعلني متوترًا.

أبسط واحد: إذا كانت جميع استخدامات ? في كتلة Try تحتوي على نفس النوع Try::Error ، فاستخدم نوع الخطأ هذا للكتلة التي تحتوي على try (ما لم ملزمة خلاف ذلك).

"(ما لم يكن ملزمًا بخلاف ذلك)" ، بالطبع ، هو الجزء المخيف الدقيق.

آمل ألا أكون غير بناء للغاية مع هذا المنشور. ومع ذلك ، أردت مقارنة مثال nikomatsakis بمثال من عالم موازٍ حيث لا يقوم ? بإجراء تحويل قسري ، ولا يوجد التفاف تلقائي لنتيجة الكتلة try :

use std::error::Error;
fn foo() -> Result<(), Box<dyn Error>> {
    let x = try {
        std::fs::File::open("foo").err_convert()?;
        Ok(())
    };

    x?;

    Ok(())
}

في هذا العالم:

  • من السهل أن ترى أن كلاً من النطاق try و fn نفسه يؤديان إلى النجاح دون أي قيمة. من السهل أيضًا أن ترى دون أن تحاول أن تنتج Result s.
  • من الواضح مكان حدوث تحويل الخطأ.
  • يمكن نقل تحويل الخطأ إلى التعبير x? ، مما يجعل النطاق try خاصًا بالعمليات std::fs::File .
  • كل نوع يشير إلى سلسلة بطلاقة من توقيع النوع. لكل من الآلة ولنا نحن البشر.
  • مطلوب تلميح الكتابة من قبل المستخدم فقط في الحالات التي نريد فيها بالفعل طي الأخطاء في أخرى مستقلة.

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

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

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

على سبيل المثال

try fn foo() -> u32 throw String {
  let result = try: u32 throw String {
    123
  };
  result?
}

آسف إذا تمت مناقشة هذا ولكن ما هي مزايا استخدام

try fn foo() -> u32 throw String { ... }

أو ما شابه ذلك مقابل

fn foo() -> Result<u32, String> { ... }

؟
فقط يبدو وكأنه بناء جملة مكرر.

gorilskij كما أفهمها ، فإن الميزة الأساسية هي الحصول على التفاف Ok . خلاف ذلك ، عليك أن تكتب:

fn foo() -> Result<u32, String> {
    try {
        // function body
    }
}

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

أنا شخصياً أريد أن أبتعد قدر الإمكان عن ظهور الاستثناءات.

هذا ليس موضوع النقاش حول try fn ، لذا من فضلك لا تأخذ هذا الظل أبعد من ذلك. هذا الموضوع مخصص للميزة المقبولة المتمثلة في كتل try ، وليس الميزة المحتملة (وحتى الآن ، ليست RFCd) ميزة try fn .

لقد لاحظت للتو أن RFC الأصلي لـ ? اقترح باستخدام Into ، وليس From . فإنه ينص:

يستخدم RFC الحالي السمة std::convert::Into لهذا الغرض (والتي تحتوي على غطاء ضمني لإعادة التوجيه من From ).

على الرغم من أنه يترك طريقة التنبيه الدقيق كمسألة لم يتم Into مفضلًا (على الأرجح) بناءً على التوجيه من From :

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

ومع ذلك ، في RFC Try trait ، لم يعد يُذكر Into ، ويتم التحويل باستخدام From بدلاً من ذلك. هذا أيضًا ما يستخدمه الكود الآن ، حتى بالنسبة لـ ?
https://github.com/rust-lang/rust/blob/b613c989594f1cbf0d4af1a7a153786cca7792c8/src/librustc_ast_lowering/expr.rs#L1232

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

fn with_user_err<E>(op: impl Fn() -> Result<(), E>) -> Result<(), MyError>
where E: Into<MyError>

ولكن إذا قمت بذلك ، فلن يمكنني استخدام ? في جسم الوظيفة. إذا أردت أن أفعل ذلك ، علي أن أكتب

fn with_user_err<E>(op: impl Fn() -> Result<(), E>) -> Result<(), MyError>
where MyError: From<E>

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

ربما فات الأوان (؟) لإصلاح ? الآن (وهو أمر مؤسف للغاية - ربما في الإصدار التالي؟) ، لكن يجب علينا على الأقل التأكد من عدم التعمق في هذا المسار في Try سمة.

jonhoocuviper حاولت تغيير desugaring من From إلى Into في # 60796 للتحقق # 38751 وأدى إلى كمية كبيرة من الكسر الاستدلال بالضبط بسبب From -> Into بطانية جعلت من الصعب على rustc التعامل مع الحالة الشائعة لتحويل الهوية. تقرر أنه لا يستحق الكثير من تكلفة كسر الاستدلال.

jonhoo قد تجد هذا التعليق من نيكو بالمعلومات:

يوجد أيضًا حد مشفر في نظام السمات. إذا كان عليك حل هدف مثل ?X: Into<ReturnType> ، فسوف نفشل في الحل ، ولكن إذا كان عليك حل هدف مثل ReturnType: From<?X> ، فمن المحتمل أن ننجح ونستنتج قيمة ?X .

تحرير: هنا ، يشير ?X إلى متغير استدلال غير معروف. الحد المشفر في نظام السمات اليوم هو أن النوع Self يجب أن يتم استنتاجه جزئيًا على الأقل حتى يتسنى لنا استكشاف هذا الخيار.

TL ؛ DR هو أن الاستنتاج بـ Into أصعب ، وبطريقة متأصلة في الطريقة التي يعمل بها حلال السمات.

KrishnaSannasi @ CAD97 شكرا لك ، هذا مفيد! ما زلت قلقًا بشأن استقرارنا كثيرًا استنادًا إلى From نظرًا لأننا نترك المنفذين من Into نوعًا ما بشكل دائم. هل توقع أن الاستدلال هنا قد يتحسن في النهاية؟ هل يجب تغيير إرشادات تفضيل Into بحدود؟ هل نتوقع أنه مع قواعد ترابط السمات الجديدة في 1.41 (أعتقد أنها كانت كذلك) لم يعد هناك سبب لتنفيذ Into ، والنظر في كل هذه الأخطاء الضمنية؟

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

هل هذا (السمة Try ) تغطي السماح لـ ? بالعمل مع السمات Into / From بحدود عامة للأنواع التي تطبق Try (على سبيل المثال Result نفسها)؟

أي ? في الإغلاق أو الوظيفة التي تُرجع مثلاً impl Into<Result>

(لا يبدو الأمر كذلك عندما أحاول ذلك ليلاً؟)

أنا حريص على رؤية هذا مستقرًا. بعد قراءة هذا الموضوع و # 70941 ، أعتقد أنه يجب تحديث الملخص على النحو التالي:

  1. "العزم ما إذا كان ينبغي كتل الصيد" يجب تكتك التفاف "قيمة النتيجة"، "حسم منصب نعم"

  2. تمت إضافة قلق جديد حول مشاكل الاستدلال هذه. ربما شيء مثل:

    • [] الصعوبات المريحة بسبب مشاكل الاستدلال بالنوع.

ISTM أنه يمكن معالجة هذا الشاغل الأخير ، من بين طرق أخرى:

  • قم بإضافة بناء جملة تشفير من النوع المفصل إلى try ، قبل التثبيت
  • قرر أن https://github.com/rust-lang/rfcs/pull/803 # 23416 (اكتب ascription) سيعالج هذا الأمر ويجب أن يستقر أولاً
  • أضف نوعًا من الرجوع التلقائي (على سبيل المثال إلى Result ، ربما كما هو مقترح في https://github.com/rust-lang/rust/issues/31436#issuecomment -614735806
  • قرر تثبيت try كما هو الآن ، تاركًا مساحة نحوية / دلالية للتحسين في المستقبل

(بعض هذه الخيارات ليست متنافية.)

شكرا لاهتمامكم وآمل أن تجد هذه الرسالة مفيدة.

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

قد يساعد الرجوع إلى Result ، لكنه لا يحل مشاكل الاستدلال مع أنواع الخطأ: try { expr? }? (أو في الواقع المعادلات الأكثر تعقيدًا) لها مكالمتان فعالتان إلى .into() ، مما يمنح المترجم مرونة كبيرة في النوع المتوسط.

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

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

في الأربعاء ، 05 آب (أغسطس) 2020 الساعة 02:29:06 مساءً -0700 ، كتب نيكو ماتساكيس:

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

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

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

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

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

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

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

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

steveklabnik @ mark-im جرب الكتل واكتب ascription لا تتعارض بأي شكل من الأشكال ، فهي ليست مسألة ميزة أو أخرى. إنها مجرد كتل try تحتوي على حالات فشل في الاستدلال من النوع غير المريح ، ويمكن أن يكون إسناد النوع المعمم وسيلة لحل هذه المشكلة ، ولكن نظرًا لأن إسناد النوع المعمم ليس ميزة قريبة المدى أو حتى أمرًا مؤكدًا ،

هذا لا يعني أننا لن نجعل التوصيف المعمم هو الحل للمشكلة ؛ أحد الخيارات الجديرة بالتحقيق هو "ثبّت المحاولة كما هي ، وتوقع أن يُحلّ هذا النوع من الكتابة في يوم من الأيام هذه المشكلة". كل ما قيل هو عدم منع التثبيت عند كتابة الوصف.


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

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

withoutboats أوافق على الحاجة إلى جمع كل المعلومات في مكان واحد والرغبة في دفع هذه الميزة إلى خط النهاية. ومع ذلك ، أعتقد أنه في المرة الأخيرة التي حققنا فيها هنا ، أصبح من الواضح أيضًا أن التغييرات على Try قد تكون صعبة بسبب التوافق مع الإصدارات السابقة - ذكر cramertj بعض Pin ، IIRC.

هل كانت هذه الصفحة مفيدة؟
0 / 5 - 0 التقييمات