Rust: مشكلة في تتبع سمات TryFrom / TryInto

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

مشكلة في التتبع لـ https://github.com/rust-lang/rfcs/pull/1542


لكى يفعل:

  • [] هل الوثائق الموجودة مرضية؟
  • [x] https://github.com/rust-lang/rust/pull/56796 قم بتغيير الحدود على TryFrom blanket لاستخدام Into بدلاً من From
  • [] (إعادة) الاستقرار العلاقات العامة
B-unstable C-tracking-issue T-libs

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

أرغب في أن يصبح ! كافيًا من نوع المفردات بحيث يُقرأ Result<_, !> حدسيًا على أنه "نتيجة معصومة" ، أو "نتيجة (حرفيًا) لا تخطئ أبدًا." يبدو لي استخدام اسم مستعار (بغض النظر عن نوع منفصل!) أمرًا زائدًا عن الحاجة ويمكنني أن أرى أنه يسبب لي على الأقل توقفًا مؤقتًا مؤقتًا عند قراءة توقيعات الكتابة - "انتظر ، كيف كان هذا مختلفًا عن ! مرة أخرى ؟ " YMMV ، بالطبع.

ال 240 كومينتر

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

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

نعتذر إذا تمت تغطية هذا في مكان آخر ، ولكن ما الذي نود رؤيته قبل وضع علامة عليه على أنه مستقر؟ أنا متأكد من أنني أعدت تطبيق هذه الوظيفة عدة مرات في مشاريع مختلفة ، لذا فإن السمة الشائعة القابلة لإعادة الاستخدام ستجعلني 😄

يمكننا طرحه للمناقشة في الدورة القادمة.

🔔 تدخل هذه المشكلة الآن في فترة تعليق نهائية لمدة دورة من أجل الاستقرار 🔔

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

لدي بعض الأسئلة حول الغرض من هذه السمات.

  1. ما هي الأنواع في std التي سيتم تنفيذها من أجلها؟
  2. ما الأنواع التي يجب أن تحصل على تطبيقات مثل impl TryFrom<T> for T ؟
  3. ما الأنواع التي يجب أن تحصل على تطبيقات مثل impl TryFrom<U> for T إذا كان لديهم بالفعل impl From<U> for T ؟

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

أعتقد بشكل عام أن الحدس يجب أن يكون هو نفسه تمامًا بالنسبة لـ From / Into ، إلا في حالة فشل التحويل. تمامًا مثلما نضيف تدريجيًا تطبيقات From و Into لأنواع المكتبات القياسية ، أتوقع أننا سنفعل الشيء نفسه لـ TryFrom و TryInto . أهم إرشادات هنا هي أن التحويل يجب أن يكون "واضحًا بشكل قانوني" - إذا كان هناك أكثر من طريقة معقولة لتحويل نوع إلى آخر ، فقد لا يكون TryFrom أو From هو الصحيح أشياء لاستخدامها.

_ بشكل عام_ ، لا أعتقد أنه يجب توقع أنه يجب على جميع الأنواع impl TryFrom<T> for T أو رفع impl From<U> for T يدويًا. على وجه الخصوص ، غالبًا ما يكون نوع الخطأ الذي يجب اختياره غير واضح. ومع ذلك ، فإن هذه الأنواع من التطبيقات هي إلى حد كبير تلك التي يمكن ويجب تحديدها لجعل واجهة برمجة تطبيقات معينة تعمل. على سبيل المثال ، لدينا كلا النوعين من التطبيقات لمجموعات مختلفة من أنواع الأعداد الصحيحة الأولية للأسباب الموضحة في RFC. كمثال آخر ، سمة مخصصة واحدة سيتم استبدالها TryFrom / TryInto هي postgres :: IntoConnectParams ، والتي لها تطبيق انعكاسي لتحويل ConnectParams إلى نفسها.

بشكل عام ، لا أعتقد أنه يجب توقع أن جميع الأنواع يجب أن تتضمن TryFrom<T> for T أو رفع ضمني From<U> for T يدويًا.

عندما يكون التحويل TryFrom معصومًا عن الخطأ ، يجب أن يكون نوع الخطأ enum Void {} ، أليس كذلك؟ (أو تعداد مشابه له اسم آخر.) والذي بالمناسبة يبدو لي سببًا جيدًا لوجود غرض عام Void اكتب std .

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

هههههههههههههههههههههههههههههههههههههههههههههههه _

sfackler ليس إذا كنت تستخدم عادي From لأنواع الخطأ (مثل try! )! impl<T> From<!> for T يمكن ويجب أن يوجد لهذا الغرض.

تمامًا مثلما نضيف تدريجيًا تطبيقات From و Into لأنواع المكتبات القياسية ، أتوقع أننا سنفعل الشيء نفسه لـ TryFrom و TryInto .

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

impl TryFrom<u32> for char
impl TryFrom<char> for u8
impl TryFrom<Vec<u8>> for String
impl TryFrom<&[u8]> for &str

_ بشكل عام_ ، لا أعتقد أنه يجب توقع أنه يجب على جميع الأنواع impl TryFrom<T> for T أو رفع impl From<U> for T يدويًا.

المشكلة هي أنه إذا كنت تريد استخدام TryInto<T> و T ليس في صندوقك ، فعليك فقط أن تأمل أن impl TryFrom<T> for T قد تم تنفيذه. هذا قيد مؤسف ، وهو قيد غير موجود لـ From / Into .

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

هل هذا يعني أن نوع الخطأ تم إصلاحه بطريقة ما بناءً على النوع الذي تقوم بالتحويل إليه؟

لماذا لا يتم تقييد TryFrom::Err و TryInto::Err بـ std::error::Error ؟

لا يحدها الأمراض المنقولة جنسيا :: خطأ :: خطأ؟

سيؤدي ذلك إلى منع Err من أن يكون () ، وهو نوع خطأ قابل للتطبيق للتحويلات المعصومة.

إذا كان () هو نوع الخطأ ، فقد ترجع الدالة Err(()) . هذا لا يجعل الدالة معصومة عن الخطأ. إنه يفشل في تقديم خطأ مفيد عندما يفشل. عند كتابة التعليمات البرمجية التي تستخدم التحويلات التي قد تكون أو لا تكون غير معصومة ، أعتقد أن أفضل طريقة هي الالتزام بـ Into وكتابة تخصص يستخدم TryInto .

بمجرد تنفيذ RFC 1216 ، سيكون ! هو نوع الخطأ المناسب للتحويلات المعصومة. أعتقد أن هذا يعني أن Error مقيد على TryFrom::Err / TryInto::Err سيكون مقبولًا.

سأحب ذلك بالتأكيد إذا أدى تنفيذ From<T> إلى تنفيذ TryFrom<T, Err = !> (وكذلك الحال مع Into بالطبع).

أعتقد أن هذا يعني أن Error ملزم على TryFrom::Err / TryInto::Err سيكون مقبولًا.

نعم ، سيكون لدينا بالتأكيد impl Error for ! { ... } لهذه الأنواع من الحالات على وجه التحديد.

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

في POV الخاص بي ، يجب تحديد جميع الأنواع المرتبطة التي تمثل أخطاء بـ Error . للأسف ، تركنا بالفعل نوعًا واحدًا بدونه: FromStr::Err (الأنواع العامة الأخرى فقط هي TryInto و TryFrom ، تم التحقق منها بـ ack 'type Err;' و ack 'type Error;' ). دعونا لا نجعل هذا مرة أخرى.

بعد مناقشته مؤخرًا ، قرر فريق libs العمل على تثبيت هذه السمات حتى يتم تحديد النوع ! .

أعتقد أنه الآن لم يعد هذا محجوبًا ، أليس كذلك؟

ستؤدي إزالة الترشيح لأن sfackler إلى التحقيق في أنواع ! وهذه السمات.

هل سيتغير هذا الأمر حتى يستقر في المستقبل المنظور؟

لماذا لا يتم تقييد TryFrom :: Err و TryInto :: Err بواسطة std :: error :: Error؟

std::err::Error غير موجود في إنشاءات #![no_std] . أيضًا ، في كثير من الحالات لا توجد فائدة من تنفيذ std::err::Error لنوع خطأ حتى عندما يكون متاحًا. وبالتالي ، فإنني أفضل ألا يكون لدي أي التزام من هذا القبيل.

لقد جربت تنفيذ هذا بنفسي في مكتبتي الخاصة. أود أن أكرر القلق الذي عبر عنه SimonSapin في https://github.com/rust-lang/rfcs/pull/1542#issuecomment -206804137: try!(x.try_into()); محيرة لأن كلمة "try" هي تستخدم طريقتين مختلفتين في نفس البيان.

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

أنا شخصياً أعتقد أنه لا يزال يتعين علينا محاولة العثور على نمط لا يتطلب البادئة try_ على الأسماء.

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

لقد أصبح بالفعل شبه قياسي لاستخدام try_ لنكهات الوظائف التي تُرجع Result .

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

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

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

أود أن أكرر القلق الذي عبر عنه SimonSapin في rust-lang / rfcs # 1542 (تعليق)

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

أيضًا ، أعتقد أن الاستقرار عاجلاً وليس أخيرًا هو أكثر أهمية من جولة أخرى من أسماء الدراجات. لقد مرت أشهر منذ أن تم قبول RFC وتم تنفيذ هذا #[unstable] .

لقد أصبح استخدام try_ مع نكهات الوظائف التي تعيد النتيجة شبه قياسي.

هذا هو الشيء الأكثر غرابة في هذه الميزة. تُرجع معظم الدوال التي أكتبها Result لكني لم أقم بتسمية أي من هذه الوظائف ببادئة try_ إلا عند محاولة تجربة هذه السمة.

أيضًا ، لم أجد أي ميزة عملية لكتابة هذا:

impl TryInto<X> for Y {
    type Err = MyErrorType;

   fn try_into(self) -> Result<X, Self::Err> { ... }
}

بدلاً من ذلك ، يمكنني دائمًا كتابة هذا ، ناهيك عن النفقات النحوية:

    fn into_x(self) -> Result<X, MyErrorType> { ... }

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

أيضًا ، وجدت أن تسمية نوع الخطأ المرتبط Err بدلاً من Error كانت عرضة للخطأ لأنني كنت أكتب دائمًا Error . لقد لاحظت أن هذا الخطأ قد حدث حتى أثناء صياغة RFC نفسها: https://github.com/rust-lang/rfcs/pull/1542#r60139383. أيضًا ، الكود الذي يستخدم Result يستخدم بالفعل الاسم Err على نطاق واسع لأنه مُنشئ Result .

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

أيضًا ، لم أجد أي ميزة عملية لكتابة هذا ...

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

impl TryInto<TcpStream> for SocketAddr {
    type Err = io::Error;
    fn try_into(self) -> Result<TcpStream, io::Error> {
        TcpStream::connect(self)
    }
}

impl<T> TryInto<MutexGuard<T>> for Mutex<T> {
    type Err = TryLockError<MutexGuard<T>>;
    fn try_into(self) -> Result<Mutex<T>, Self::Err> {
        self.try_lock()
    }
}

impl TryInto<process::Output> for process::Child {
    type Err = io::Error;
    fn try_into(self) -> Result<process::Output, io::Error> {
        self.wait_with_output()
    }
}

impl TryInto<String> for Vec<u8> {
    type Err = FromUtf8Error;
    fn try_into(self) -> Result<String, FromUtf8Error> {
        String::from_utf8(self)
    }
}

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

أعتقد أن وجود معلمات TryInto<...> أو TryFrom<...> يبدو وكأنه نموذج مشكوك فيه.

نتفق أيضا. لماذا لا تقوم فقط بالتحويل ومعالجة الخطأ قبل تمرير القيمة إلى الوظيفة؟

std :: err :: Error غير موجود في #! [no_std] بناءات. أيضًا ، في كثير من الحالات ، لا توجد فائدة من تنفيذ std :: err :: Error لنوع خطأ حتى عندما يكون متاحًا. وبالتالي ، فإنني أفضل ألا يكون لدي أي التزام من هذا القبيل.

الميزة الوحيدة لقيودك بـ std::error::Error هي أنه يمكن أن تكون القيمة المرجعة لخطأ آخر cause() . لا أعرف حقًا سبب عدم وجود core::error::Error ، لكنني لم أبحث في ذلك.

أيضًا ، وجدت أن تسمية نوع الخطأ المرتبط Err بدلاً من Error كان عرضة للخطأ لأنني كنت أكتب دائمًا خطأ. لقد لاحظت أن هذا الخطأ قد حدث حتى أثناء صياغة RFC نفسها: rust-lang / rfcs # 1542 (تعليق). أيضًا ، الكود الذي يستخدم النتيجة يستخدم بالفعل الاسم Err على نطاق واسع لأنه مُنشئ النتيجة.

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

سواء أكان TryFrom و TryInto عامان جدًا أم لا ، أود حقًا رؤية تحويلات غير معصومة ، على الأقل بين أنواع الأعداد الصحيحة ، في المكتبة القياسية. لدي صندوق لهذا ، لكنني أعتقد أن حالات الاستخدام تذهب بعيدًا بما يكفي لجعلها قياسية. مرة أخرى عندما كان Rust هو alpha أو beta ، أتذكر استخدام FromPrimitive و ToPrimitive لهذا الغرض ، لكن هذه السمات واجهت مشاكل أكبر.

لا يمكن نقل Error إلى core بسبب مشاكل التماسك.

أيضًا بسبب مشكلات الاتساق ، لا يقوم Box<Error> بتنفيذ Error ، وهو سبب آخر لعدم ربطنا بالنوع Err .

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

where T: TryInto<Foo>, T::Err: Error

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

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

ومع ذلك ، أعتقد أن str :: parse ستستفيد بشكل كبير من هذا ، لأنها تخصص أيضًا سمة FromStr كحدود - وهي بالضبط ( TryFrom<str> ) اليد المتخصصة.

لذا صححني إذا كنت مخطئًا ، لكنني أعتقد أن تثبيت هذا واستخدامه مقابل str::parse سوف:

  1. قم بإزالة إعادة تنفيذ النموذج المعياري ، وبالتالي إزالة سمة أقل شيوعًا وتخصصًا
  2. أضف TryFrom<str> في التوقيع ، وهو موجود بشكل صحيح في وحدة التحويل ويكون أكثر وضوحًا ما يفعله
  3. سيسمح للعملاء المنبعين بتنفيذ TryFrom<str> لأنواع البيانات الخاصة بهم والحصول على str.parse::<YourSweetDataType>() مجانًا ، إلى جانب try_from الآخرون الذين يشعرون برغبة في التنفيذ ، مما يجعل تنظيم الكود المنطقي أفضل.

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

  1. اريد نسخة افتراضية؛ دعني أصل إلى SomeType::default()
  2. أريد تحويل هذا ، أتساءل عما إذا تم تنفيذ SomeType::from(other) (يمكنك فقط كتابته ومعرفة ما إذا كان يتم تجميعه ، دون الوصول إلى الوثائق)
  3. أريد استنساخ هذا clone()
  4. أريد أن أحاول الحصول على هذا من هذا ، وبما أن الأخطاء جزء لا يتجزأ من الصدأ ، فقد تفشل في التوقيع ، لذا دعني try_from - أوه انتظر: P

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

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

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

وهذا كل ما يجب أن أقوله عن ذلك ؛)

تم حظر هذا سابقًا في تحقيق في إمكانية استخدام ! كنوع خطأ لتحويلات أعداد صحيحة معصومة. نظرًا لتطبيق الميزة حاليًا ، فإن هذا يفشل حتى في أبسط الحالات: https://is.gd/Ws3K7V.

هل ما زلنا نفكر في تغيير أسماء الطرق ، أم ينبغي علينا وضع هذه الميزة في FCP؟

sfackler يعمل رابط الملعب هذا بالنسبة لي إذا قمت بتغيير نوع الإرجاع في السطر 29 من Result<u32, ()> إلى Result<u32, !> : https://is.gd/A9pWbU لا يمكنني التعرف على أن let Ok(x) = val; هو نمط لا يمكن دحضه عندما يكون val به نوع Err! ، لكن هذا لا يبدو كمشكلة حظر.

Ixrec كان الدافع الأساسي لهذه السمات هو التحويلات من وإلى محرف C عدد صحيح. إذا كان لدي وظيفة

fn foo(x: i64) -> Result<c_long, TryFromIntError> {
    x.try_into()
}

سيؤدي هذا إلى تجميع أهداف i686 ولكن ليس على أهداف x86_64.

وبالمثل ، لنفترض أنني أريد استبدال النوع IntoConnectParams : https://docs.rs/postgres/0.13.4/postgres/params/trait.IntoConnectParams.html. كيف يمكنني القيام بذلك إذا كان هناك بطانية impl<T> TryFrom<T> for T { type Error = ! } ؟ أحتاج إلى التنفيذ الانعكاسي لـ ConnectParams ، ولكن بنوع خطأ ملموس مختلف عن ! .

إنه لا يدرك أن let Ok(x) = val; هو نمط لا يمكن دحضه عندما يكون val به نوع Err!

لاحظ أن هناك علاقات عامة مفتوحة لذلك .

إذا كان لدي وظيفة ...

هذا يجب أن يعمل على الرغم من

fn foo(x: i64) -> Result<c_long, TryFromIntError> {
    let val = x.try_into()?;
    Ok(val)
}

مع وجود تعليق مزعج +1 ، أريد فقط أن أذكر أنه بعد وصول وحدات الماكرو 1.1 في Rust 1.15 ، ستكون try_from هي الميزة الأخيرة التي تبقي Ruma في حالة الصدأ الليلي. من المتوقع بفارغ الصبر try_from المستقر!

في ملاحظة أكثر جوهرية ...

هذا هو الشيء الأكثر غرابة في هذه الميزة. تُرجع معظم الوظائف التي أكتبها نتيجة ولكني لم أقم بتسمية أي من هذه الوظائف ببادئة try_ إلا عند محاولة تجربة هذه السمة.

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

أيضًا ، وجدت أن تسمية نوع الخطأ المرتبط Err بدلاً من Error كان عرضة للخطأ لأنني كنت أكتب دائمًا خطأ. لقد لاحظت أن هذا الخطأ قد حدث حتى أثناء صياغة RFC نفسها: rust-lang / rfcs # 1542 (تعليق). أيضًا ، الكود الذي يستخدم النتيجة يستخدم بالفعل الاسم Err على نطاق واسع لأنه مُنشئ النتيجة.

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

canndrew بالطبع من الممكن العمل ، ولكن الهدف الكامل من هذا الدافع لهذه الميزة هو تسهيل التعامل بشكل صحيح مع هذه الأنواع من الاختلافات في النظام الأساسي - نريد تجنب المساحة الكاملة لـ "compiles on x86 ولكن ليس ARM" .

أعتقد أنني اخترت Err للتوافق مع FromStr لكنني سأكون سعيدًا جدًا بالتبديل إلى Error قبل الاستقرار!

الميزة الأخيرة التي تحافظ على Ruma في حالة الصدأ الليلي

وبالمثل ، فإن الواجهة الخلفية للملعب البديل تحتاج فقط كل ليلة للوصول إلى TryFrom ؛ كانت عناصر serde الليلية غير مستقرة للغاية بالنسبة لي ، لذلك انتقلت إلى إعداد نص الإنشاء. مع 1.15 ، سأعود إلى #[derive] . أنتظر بفارغ الصبر أن تصبح هذه الميزة مستقرة!

sfackler آسف لم أكن أتابعها. في حالة تحويلات الأعداد الصحيحة ، يبدو أن ما نحتاجه حقًا هو عدم كتابة c_ulong إما u32 أو u64 اعتمادًا على النظام الأساسي ولكن بطريقة ما لجعله نوعًا جديدًا. بهذه الطريقة لا يمكن أن تتداخل أداة $ # TryFrom<c_ulong> مع أداة TryFrom<u{32,64}> .
بعد كل شيء ، سنواجه دائمًا مشكلات "تجميع نظام أساسي واحد وليس الآخر" إذا حددنا الأنواع بشكل مختلف على منصات مختلفة. إنه لأمر مخز أن تضطر إلى التضحية بضمانة TryFrom<T> for U where U: From<T> المنطقية تمامًا حتى نتمكن من دعم ما يبدو أنه ممارسة مشكوك فيها.

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

وبالمثل ، لنفترض أنني أريد استبدال نوع IntoConnectParams:

ما هي المشكلة هنا بالرغم من ذلك؟ لماذا لا تستخدم نوع خطأ واحد لـ TryFrom<ConnectParams> وآخر لـ TryFrom<&'a str> ؟

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

هل يوجد impl<T> From<!> for T في المكتبة القياسية؟ لا أراها في المستندات ولكن قد يكون هذا مجرد خطأ في rustdoc. إذا لم يكن موجودًا ، فلا توجد طريقة لتحويل TryFrom<ConnectParams> impl's ! Error إلى الذي أحتاجه بالفعل.

بعد أن حاولت وفشلت في التقاط أغلفة جديدة من نوع صحيح مماثل مثل Wrapping ، هناك تكاليف مريحة هائلة.

كنت أفكر في شيء أشبه بالقدرة على تحديد أنواع الأعداد الصحيحة الخاصة بك. لا. C ++:

trait IntLiteral: Integer {
    const SUFFIX: &'static str;
    const fn from_bytes(is_negative: bool, bytes: &[u8]) -> Option<Self>; // or whatever
}

impl IntLiteral for c_ulong {
    const SUFFIX: &'static str = "c_ulong";
    ...
}

extern fn foo(x: c_ulong);

foo(123c_ulong); // use a c_ulong literal
foo(123); // infer the type of the integer

هل سيؤدي ذلك إلى حل معظم المشكلات المريحة؟ لا أحب في الواقع القيم الحرفية التي يحددها مستخدم C ++ - أو الميزات بشكل عام التي تمنح الناس القدرة على تغيير اللغة بطرق مربكة - لكنني أعتقد أنه قد ينتهي بهم الأمر إلى أن تكون أهون الشرين.

هل يوجد impl<T> From<!> for T في المكتبة القياسية؟

ليس حاليًا لأنه يتعارض مع الضمنية From<T> for T . ما أفهمه هو أن التخصص الضمني يجب أن يكون قادرًا في النهاية على التعامل مع هذا بالرغم من ذلك.

هذا يبدو كشيء يجب أن نعود إليه في غضون عامين.

ما هو الجدول الزمني لتثبيت التخصص و ! ؟

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

canndrew أوافق بالتأكيد على أنه لا ينبغي تنفيذه على كل شيء. يقول المستندات _ محاولة إنشاء Self عن طريق تحويل_ ، ولكن ما الذي يعتبر تحويلاً؟ ماذا عن ... _تغيير نفس الشيء من تمثيل إلى آخر ، أو إضافة أو إزالة غلاف_؟ يغطي هذا Vec<u8> -> String و Mutex<T> -> MutexGuard<T> ، بالإضافة إلى أشياء مثل u32 -> char و &str -> i64 ؛ مع استبعاد SocketAddr -> TcpStream و process::Child -> process::Output .

أشعر أن impl From<T> for U ربما يعني TryFrom<T, Err=!> for U . بدون ذلك ، الدوال التي تتطلب TryFrom<T> s لا يمكنها أيضًا أن تأخذ From<T> s. (من المحتمل أن يكون استخدام try_from() | try_into() للتحويل الملموس المعصوم مجرد نمط مضاد. قد _ يمكنك _ القيام بذلك ، لكن ... سيكون سخيفًا ، لذا لا تفعل ذلك .)

أشعر أنني ضمني منل U ربما يعني TryFromمن اجلك.

متفق.

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

يبدو وكأنه لينت كليبي محتمل.

BlacklightShining أعتقد أنه ينبغي تنفيذه للأنواع حيث يكون "التحويل" واضحًا ، نظرًا لنوع المخرجات. في أقرب وقت ممكن تحويلات متعددة (utf8 / 16/32؟ serialising vs casting؟ etc ...) ، يجب تجنب ذلك.

IMHO:

في الوقت الحالي ، يمكننا بسهولة توفير تطبيق شامل TryFrom<&str> لكل شيء يستخدم FromStr :

impl<'a, T: FromStr> TryFrom<&'a str> for T {
    type Err = <T as FromStr>::Err;
    fn try_from(s: &'a s) -> Result<T, Self::Err> {
        T::from_str(s)
    }
}

أود أن أرى شيئًا مثل هذا مضافًا قبل استقرار try_from ، أو على الأقل بعض الإشارات إليه في المستندات. خلافًا لذلك ، قد يكون من المربك أن يكون هناك اختلاف في تطبيقات TryFrom<&'a str> و FromStr .

أوافق على أن هذا سيكون تضمينًا جيدًا ، ولكنه قد يتعارض مع المحاولة المقترحة -> TryFrom الضمنية؟

sfackler يحتمل. سيكون الأمر جيدًا تمامًا إذا أشارت كل من TryFrom و TryInto و FromStr إلى بعضها البعض في المستندات ، لكن قلقي الرئيسي هو أنه لا يوجد معيار حالي يوضح ما إذا كان str::parse و str::try_into ترجع نفس القيمة.

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

على سبيل المثال ، لنفترض أن شخصًا ما أنشأ بنية Password لموقع ويب. قد يفترضون أن "password".parse() سيتحقق من كلمة المرور للتأكد من صحتها ، ثم يحولها إلى تجزئة ، بينما Password::try_from("1234abcd") قد يفترض أن "1234abcd" هو بالفعل تجزئة مخزنة في قاعدة البيانات وحاول لتحليلها مباشرة إلى تجزئة يمكن مقارنتها.

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

على الرغم من أن فريق اللغة قد اقترح إغلاق RFC لإهمال المعلمات المجهولة ، يبدو أنهم جميعًا متفقون على أنه من الناحية المثالية سنتوقف عن إنشاء رمز جديد يستخدم معلمات مجهولة. مع أخذ ذلك في الاعتبار ، هل يمكننا تحديث توقيع try_from / try_into لإعطاء أسماء المعلمات؟ أم أنه من المهم الحفاظ على التناظر مع from / into ؟

أيضا ، هل سيكون من المفيد كتابة ملخص للأسئلة الرئيسية التي لم يتم الرد عليها والتي لا تزال قائمة؟ آمل حقًا أن نقرر تثبيت هذا في دورة الإصدار التالية. كما ذكرت ، إنها الميزة الليلية الوحيدة المتبقية التي أستخدمها كثيرًا. :}

تضمين التغريدة تريد إرسال العلاقات العامة مع إضافة بعض أسماء المعلمات؟

فيما يتعلق بالحالة الحالية للأشياء ، أعتقد أن السؤال الوحيد الذي لم تتم الإجابة عليه هو ما إذا كان ينبغي أن يكون هناك ملف

impl<T, U> TryFrom<U> for T
    where T: From<U>
{
    type Error = !;

    fn try_from(u: U) -> Result<T, !> {
        Ok(T::from(u))
    }
}

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

اكتب خطأ =! ؛

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

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

أنا أرى أن TryFrom<str> و FromStr يجب أن يكونا متطابقين وظيفيًا ، ويجب أن توضح الوثائق أن الهدف من تنفيذ الاثنين هو أن يكون متطابقًا. يجب أن يمنحك تنفيذ أحدهما الآخر ، على الأقل من حيث السماح لك باستخدام str::parse . إذا كان لدى Rust TryFrom من البداية ، فلن تكون هناك حاجة إلى FromStr . لهذا السبب ، أود أيضًا أن أوثق TryFrom<str> ليكون النموذج المفضل للكود الجديد.

jimmycuadra في هذه الحالة ، يجب علينا تعديل parse لاستخدام TryFrom ثم وضع غلاف شامل لـ FromStr -> TryFrom .

إذا كنا بصدد تغيير str::parse ليتم تنفيذه من حيث TryFrom ، فهل يجب علينا أيضًا تغيير تطبيقات أخرى FromStr للأنواع الملموسة بشكل مشابه (على سبيل المثال ، جميع المنفذين في هذه القائمة : https://doc.rust-lang.org/stable/std/str/trait.FromStr.html)؟ هل يجب تحديث مستندات FromStr لاقتراح استخدام TryFrom بدلاً من ذلك؟ هل ينبغي لعمليات التنفيذ الملموسة الحالية لـ FromStr نقل مستنداتها إلى الإصدار TryFrom ؟

أعتقد أنه من أجل التوافق مع الإصدارات السابقة ، لا يمكننا تغيير FromStr ، أليس كذلك؟

إزالة FromStr لصالح TryFrom<&str> هو بالتأكيد شيء يجب مراعاته في Rust 2.0.

نعم ، بمجرد استقرار هذه الميزة ، سنقوم بتقديم مشكلة ووضع علامة عليها بقائمة أمنيات 2.0-breakage.

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

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

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

أتوقع أن وجود T : From<U> سيضع U : TryFrom<T> في فئة تغيير " ثقب API الواضح " عندما يكون التنفيذ معقولًا.

هذا يعني أنه يجب أن يكون هناك على الأقل T : TryFrom<T> مع Error = ! ، لكن من الواضح أن إصدار أي معصوم From أفضل من ذلك.

لا يوجد بالفعل تمييز واضح بين IMO بين ما إذا كان يجب أن يوفر TryFrom From $ # $ 1 $ # $ أو ما إذا كان يجب أن يوفر From TryFrom $ # $ 3 $ # $.

لأنه من ناحية ، يمكنك اعتبار أن T::from(val) هو T::try_from(val).unwrap() فقط ، ومن ناحية أخرى ، يمكنك اعتبار T::try_from(val) Ok(T::from(val)) فقط. ايهما افضل؟ انا لا اعرف.

يمكنك اعتبار أن T::from(val) هو T::try_from(val).unwrap() فقط

أنا لا أتفق مع هذا. لا يُتوقع أن تتسبب عمليات تنفيذ From في إثارة الذعر مطلقًا. فقط الطريقة الأخرى حول منطقية.

clarcharr لأنه لا يجب أن تنزعج من ، الخيارات هي From من حيث TryFrom<Error=!> أو العكس. لكني أكره أن أحصل على النصيحة المعتادة "يجب عليك تنفيذ TryFrom مع type Error = ! " بدلاً من "يجب عليك تنفيذ From ".

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

تضمينrfcbot fcp

اقترح عضو الفريق sfackler دمج هذا. الخطوة التالية هي المراجعة من قبل بقية الفرق الموسومة:

  • [x] @ بورنتسوشي
  • [x] Kimundi
  • [x]alexcrichton
  • [x]aturon
  • [x]brson
  • [x]sfackler

لا مخاوف مدرجة حاليا.

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

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

sfackler : فقط لتسجيل الوصول ، هل نحن جيدون بشأن مخاوف مختلفة حول ! والبطانية الضمنية؟ أعلم أننا تحدثنا عن هذا في اجتماع libs ، لكن سيكون من المفيد الحصول على ملخص هنا.

aturon كانت المناقشة الأخيرة حول ذلك sfackler تتساءل عما إذا كان يجب أن يوفر $ impl From<T> for U impl TryFrom<T> for U حيث TryFrom::Error = ! .

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

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

أعتقد أن المجموعة الحالية من الضمانات لا يمكن الدفاع عنها. يمكنني أن أفهم أيًا من هذه المواقف:

  1. الغرض من TryFrom هو إجراء تحويلات غير معصومة _ فقط_ ، لذلك لا تحتوي على أشياء مثل u8 -> u128 أو usize -> usize .
  2. الغرض من TryFrom هو _جميع_ التحويلات ، وبعضها معصوم عن الخطأ وبالتالي يكون نوع TryFrom::Error غير مأهول.

لكن في الوقت الحالي ، أصبحت الأشياء في حالة هجينة فردية حيث سيقوم المترجم بإدخال رمز التحقق لتحويل i32 -> i32 ومع ذلك لا يمكنك إجراء تحويل String -> String .

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

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

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

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

ما هي الاعتراضات على! كنوع خطأ؟

تفصلنا عن ! شهور عن الاستقرار. اختر واحدًا من تثبيت TryFrom في المستقبل القريب أو الحصول على impl<T, U> TryFrom<U> for T where T: From<U> .

هل فكرنا في استخدام الأسماء المستعارة للسمات (rust-lang / rfcs # 1733) هنا؟ عندما يهبط ذلك ، يمكننا تسمية From<T> إلى TryFrom<T, Error=!> ، مما يجعل السمتين واحدة ونفس الشيء.

lfairy من شأنه أن يكسر المستخدم impl s من From ، للأسف.

glaebhoerl نعم ، أنت محق 😥 يشير قسم التحفيز في RFC إلى تسمية مستعارة impl s لكن الاقتراح الفعلي لا يسمح بها.

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

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

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

اختر واحدًا من تثبيت TryFrom في المستقبل القريب أو الحصول على impl<T, U> TryFrom<U> for T where T: From<U> .

أعتقد أن السؤال الأساسي هنا هو ما إذا كانت التحويلات المعصومة تنتمي إلى السمة. أعتقد أنهم يفعلون ذلك ، لأشياء مثل التحويلات المعصومة في RFC وللاستخدام العام (على غرار ما يبدو أنه عديم الفائدة T:From<T> ). بالنظر إلى ذلك ، فإن أكثر ما أريده هو تجنب عالم يُتوقع فيه أن يصل كل منفذ من النوع impl TryFrom<MyType> for MyType ، ويجب أيضًا أن ينتج عن كل ضمانة From TryFrom . (أو احصل على الأخطاء لاحقًا لعدم توفيرها.)

فهل يمكن أن نحصل على البطانية بدون تثبيت ! ؟ أعتقد أن هناك طريقة ، نظرًا لأن لدينا بالفعل أنواع تشبه ! في المكتبة ، مثل std::string::ParseError . (" هذا التعداد محرج بعض الشيء: لن يكون موجودًا في الواقع. ")

رسم تخطيطي لكيفية عمل ذلك:

  • نوع جديد ، core::convert::Infallible ، تم تنفيذه تمامًا مثل std::string::ParseError . (ربما يمكنك تغيير الأخير إلى اسم مستعار من النوع الأول).
  • impl<T> From<Infallible> for T بحيث يكون متوافقًا في ? مع أي نوع خطأ (راجع العناصر c_foo لاحقًا)
  • استخدم Infallible كنوع Error في الضمانة الشاملة
  • لاحقًا ، ضع في اعتبارك type Infallible = !; كجزء من تثبيت النوع أبدًا

سوف أتطوع للقيام بذلك ، إذا كان من المفيد تجسيد ذلك.

بالنسبة إلى c_foo : سيستمر ما سبق في السماح برمز مثل هذا:

fn foo(x: c_int) -> Result<i32, TryFromIntError> { Ok(x.try_into()?) }

لكنها ستجعل رمزًا مثل هذا قابلاً للنقل "footgun" ، بسبب أنواع الأخطاء المختلفة

fn foo(x: c_int) -> Result<i32, TryFromIntError> { x.try_into() }

أنا شخصياً لا أشعر بالقلق من هذا الاختلاف لأنه طالما أن c_int هو اسم مستعار من النوع ، فهناك مسدس قدم "تلقائي كامل":

fn foo(x: c_int) -> i32 { x }

وبشكل عام ، فإن الكود الذي يتوقع أن يكون النوع المرتبط في سمة ما هو نفسه بالنسبة للضمانات المختلفة يبدو وكأنه رائحة كود بالنسبة لي. قرأت TryFrom كـ "تعميم From "؛ إذا كان الهدف هو "تحويلات أفضل بين مجموعات فرعية للأعداد الصحيحة" - الأمر الذي يبدو مفيدًا أيضًا - فإن "نوع الخطأ نفسه دائمًا" يكون منطقيًا ، لكنني أتوقع شيئًا مستهدفًا بـ std::num بدلاً من ذلك ، ربما مثل num::cast::NumCast (أو boost::numeric_cast ).

(بصرف النظر: مع دمج #[repr(transparent)] في FCP ، ربما يمكن أن تصبح أنواع c_foo أنواعًا جديدة ، وعند هذه النقطة يمكن أن تكون هذه التحويلات أكثر اتساقًا. يمكن أن تقوم الضمانات From & TryFrom بتدوين C "char <= short < = int <= long "، بالإضافة إلى الأحجام القياسية المطلوبة مثل c_int:From<i16> أو c_long:TryFrom<i64> . ثم التحويل أعلاه سيكون i32:TryFrom<c_int> على الكل المنصات ، مع نفس النوع Error دائمًا ، وتختفي المشكلة.)

بخصوص "هذا التعداد محرج بعض الشيء: لن يكون موجودًا في الواقع".

هل هناك سبب يجعل نوع الخطأ لا يمكن أن يكون مجرد وحدة؟ لماذا تهتم ببنية ParseError الفريدة إذا كانت المحادثة لا يمكن أن تخطئ أبدًا؟

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

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

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

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

أعتقد أن السؤال الأساسي هنا هو ما إذا كانت التحويلات المعصومة تنتمي إلى السمة.

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

let cfg = config![
    BoolOpt::SomeCfgKey => true,
    "SomeOtherCfgKey" => 77,
];

باختصار القصة الطويلة ، ينتهي الأمر بالماكرو بالتوسع إلى قائمة مكالمات ($k, $v).into() . أرغب في إجراء فحص في التحويل لمفاتيح السلسلة للتأكد من أنها تسمي خيار تهيئة صالحًا ، أي تنفيذ TryFrom<(String, ???)> وتغيير الماكرو لاستخدام ($k, $v).try_into() . سيكون من الصعب القيام بكل هذا إذا لم يكن هناك اسم طريقة واحد للماكرو لاستخدامه لجميع التحويلات.

: bell: هذا يدخل الآن فترة التعليق النهائية ، وفقًا للمراجعة أعلاه . :جرس:

أعجبتني حقًا فكرة:

impl<U: TryFrom<T, Error=!>> From<T> for U {
    fn from(val: T) -> U {
        val.unwrap()
    }
}

لأن أي شخص يريد TryFrom<Error=!> يمكنه تنفيذه ، لكن لا يزال بإمكان الأشخاص تنفيذ From إذا رغبوا في ذلك. ربما يمكننا في النهاية إهمال From لكننا لسنا مضطرين لذلك.

تبدو خطة scottmcm لاستخدام تعداد فارغ رائعة بالنسبة لي.

@ Ericson2314 كتبت :

ليس إذا كنت تستخدم عادي From لأنواع الخطأ (على سبيل المثال ، try! )! impl<T> From<!> for T يمكن ويجب أن يوجد لهذا الغرض.

كيف سيعمل هذا في الممارسة؟ لنفترض أنني أحاول كتابة دالة مثل هذا:

fn myfn<P: TryInto<MyType>>(p: P) -> Result<(), MyError>

باستثناء أن هذا بالطبع لا يعمل ، أحتاج إلى تحديد Error= على TryInto . لكن ما هو النوع الذي يجب أن أكتب هناك؟ يبدو MyError واضحًا ولكن بعد ذلك لا يمكنني استخدام MyType مع البطانية TryFrom .

هل تقترح ما يلي؟

fn myfn<E: Into<MyError>, P: TryInto<MyType, Error=E>>(p: P) -> Result<(), MyError>

هذا يبدو مطولا جدا.

هناك سؤال أكثر عمومية حول كيفية عمل ذلك إذا كنت أريد تحويلات متعددة "معصومة" لنفس النوع ولكن مع أنواع أخطاء مختلفة.

ربما يجب تغيير تعريف TryFrom إلى ما يلي:

pub trait TryFrom<T, E>: Sized {
    type Error: Into<E>;

    fn try_from(t: T) -> Result<Self, E>;
}

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

fn myfn<P: TryInto<MyType>>(p: P) -> Result<(), MyError> where MyError: From<P::Error>

لا يزال الأمر مطولًا بعض الشيء ، ولكنه يوضح قيود استدعاء الوظيفة بشكل جيد (مساحة اللعب )

تحرير: والمحاولة باستخدام متغير مثل P::Error: Into<MyError> لا تعمل فعليًا مع ? نظرًا لعدم وجود تطبيق شامل From مقابل Into . عند تغيير TryFrom كما أظهرت ، أتوقع أن أواجه نفس المشكلة.

فترة التعليق الأخيرة قد اكتملت الآن.

jethrogb هيه لقد نقلت عني منذ عام مضى لذا كان علي التفكير قليلاً. @ Nemo157 صحيح تمامًا ويبدو ذلك معقولًا.

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

تعليق على هذا من قضية أخرى: https://github.com/rust-lang/rust/pull/41904#issuecomment -300908910

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

تحدث فريق libs عن هذا مرة أخرى قبل أسبوعين (آسف للتأخير في كتابة هذا)! لقد توصلنا إلى استنتاج مفاده أن مصدر المشكلات المتعلقة بهذه الميزة كان في المقام الأول أن حالة FFI لا تتناسب حقًا مع أي حالة استخدام أخرى لهذه السمات - إنها فريدة من نوعها من حيث أنك تسميها على أنواع محددة من خلال الأسماء المستعارة التي تختلف على أساس الهدف.

لذا ، فإن خطة العمل الأساسية هي إضافة impl<T, U> TryFrom<T> for U where U: From<T> وإزالة الضمانات الصريحة لتحويلات الأعداد الصحيحة غير القابلة للخطأ. للتعامل مع حالة استخدام FFI ، سنقوم بإنشاء واجهة برمجة تطبيقات منفصلة.

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

لقد فتحت PR لـ "API المنفصل" لأنواع التكامل: https://github.com/rust-lang/rust/pull/42456

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

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

من فضلك لا تقم بإزالة هذا.

لم أضطر أبدًا إلى كتابة رمز عام تم تحديد معلماته بواسطة TryInto أو TryFrom

حتى لو كان الأمر كذلك ، لا أعتقد أن هذا يجعل TryInto و TryFrom أقل فائدة بكثير. أستخدم Into و From في كل مكان في السياقات غير العامة. تبدو إضافة impl s من سمات المكتبة القياسية أكثر "طبيعية" و "متوقعة" من مجموعة من طرق التحويل المتأصلة المخصصة.

لم أضطر أبدًا إلى كتابة رمز عام تم تحديد معلماته بواسطة TryInto أو TryFrom

من أحد مشاريعي:

pub fn put_str_lossy<C, S> (&self, s: S)
    where C: TryInto<ascii::Char>,
          S: IntoIterator<Item = C>
{
    for c in s.into_iter() {
        self.put_char(match c.try_into() {
            Ok(c) => c,
            Err(_) => ascii::QUESTION_MARK,
        });
    }
}

هل من المتوقع أن يتبع تطبيق هذه السمات أي قوانين معينة؟ على سبيل المثال ، إذا تمكنا من تحويل A إلى B و B إلى A ، فهل يلزم أن يكون التحويل قابلاً للعكس عندما ينجح ؟:

#![feature(try_from)]

use std::convert::{TryFrom, TryInto};

fn invertible<'a, A, B, E>(a: &'a A) -> Result<(), E>
    where A: 'a + TryFrom<&'a B>,
          A: PartialEq,
          B: 'a + TryFrom<&'a A>,
          E: From<<A as TryFrom<&'a B>>::Error>,
          E: From<<B as TryFrom<&'a A>>::Error>,
{
    let b = B::try_from(a)?;
    let a2 = A::try_from(&b)?;
    assert!(a == &a2);
    Ok(())
}

تحرير: ق / انعكاسي / معكوس /

briansmith نظرًا لكيفية عمل From ، سأقول إنه ليس قابلاً للعكس بهذه الطريقة.

use std::collections::BinaryHeap;

fn main() {
    let a = vec![1, 2];
    let b = BinaryHeap::from(a.clone());
    let c = Vec::from(b);
    assert_ne!(a, c);
}

لذلك أنا أتساءل عن التطبيقات الحالية لهذه السمة. كما جاء في # 43127 (انظر أيضًا # 43064) ، لا أعرف ما إذا كان ذلك بسبب استخدام التطبيق لـ i / u128 ، ولكن يبدو أن استدعاءات TryInto ليست مضمنة. هذا ليس بالأمر الأمثل ، ولا يتماشى حقًا مع روح التجريد من دون تكلفة. على سبيل المثال ، يجب تحسين استخدام <u32>::try_into<u64>() وصولاً إلى علامة بسيطة تمتد في التجميع النهائي (بشرط أن يكون النظام الأساسي 64 بت) ، ولكن بدلاً من ذلك ينتج عنه استدعاء دالة.

هل هناك شرط أن تكون تطبيقات السمة واحدة لجميع أنواع الأعداد الصحيحة؟

وفقًا لـ # 42456 ، من المحتمل ألا يكون لدينا impl TryFrom مباشرة على أنواع الأعداد الصحيحة ، ولكن كيف تبدو سمة "NumCast" (التي يجب أن تتحول # 43127) إلى قيد الصياغة.

بغض النظر عما إذا كان سينتهي بهم المطاف بالانتقال إلى سمة أخرى ، يتم تنفيذ هذه التحويلات اليوم في libcore ويمكن استخدامها في libcore. أعتقد أن استخدام u128 / i128 لجميع تحويلات الأعداد الصحيحة قد تم من أجل بساطة كود المصدر. ربما يجب أن يكون لدينا بدلاً من ذلك رمز مختلف اعتمادًا على ما إذا كان نوع المصدر أوسع أو أضيق من الوجهة. (ربما مع استدعاءات ماكرو مختلفة بناءً على #[cfg(target_pointer_width = "64")] مقابل #[cfg(target_pointer_width = "32")] مقابل #[cfg(target_pointer_width = "16")] .)

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

لم أكن أدرك أنه كان هناك موافقة من فريق libs على أنه يمكننا استخدام فكرة scottmcm كحل بديل لنوع الدوي غير المستقر. يمكنني العمل على PR لإجراء التغييرات sfackler المذكورة باستخدام الحل البديل.

رائع ، شكرًا jimmycuadra!

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

هناك تحويلات رائعة أخرى يمكن أن تستفيد من هذه السمات مثل TryFrom<&[T]> مقابل &[T; N] . لقد قدمت مؤخرًا بيانًا عامًا لتنفيذ هذا بالضبط: https://github.com/rust-lang/rust/pull/44764.

تحويلات كهذه مهمة بما يكفي بالنسبة لي لاستقرار TryFrom .

مع دمج # 44174 ، أعتقد أنه تم إلغاء حظر هذا الآن.

قام PR هذا بإزالة التنفيذ التلقائي لـ FromStr لأي نوع يستخدم TryFrom<&str> لأن نظام النوع لا يمكنه دعمه حاليًا ، حتى مع التخصص. القصد من ذلك هو إهمال FromStr و parse لصالح TryFrom<&str> و try_into بمجرد استقرار هذه الميزة. من المؤسف أن نفقد التوافق المؤقت بين الاثنين - إذا كان لدى أي شخص أفكار لسد الفجوة ، يرجى التحدث.

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

و PR لإيقاف FromStr / parse.

لا ينبغي إضافة تحذيرات الإيقاف إلى Nightly حتى يتوفر البديل على Stable (أو حتى يتم تنفيذ https://github.com/rust-lang/rust/issues/30785) ، بحيث يكون من الممكن في أي وقت عمل بناء قفص دون تحذيرات على جميع قنوات الإصدار الثلاثة.

فاتني العلاقات العامة الأخرى لأن المراجع لا تؤدي إلى إشعارات البريد الإلكتروني. لاحظت أن هناك impl From<Infallible> for TryFromIntError محددًا. ألا يجب أن يكون هذا impl<T> From<Infallible> for T كما تمت مناقشته؟

jethrogb للأسف ، هذا يتعارض مع impl<T> From<T> for T لذلك لا يمكن القيام به (حتى نحصل على التقاطع يعني؟ - واستخدام ! لا يعمل أيضًا هناك).

آه scottmcm بالطبع.

لا أعتقد أنك بحاجة إلى إشارات التقاطع؟ أليس هذا مجرد تخصص مباشر؟

لم أقرأ التعليقات الأخرى ، لكن TryFrom معطل بالنسبة لي الآن (كان يعمل جيدًا من قبل).

نسخة rustc:

rustc 1.22.0-nightly (d6d711dd8 2017-10-10)
binary: rustc
commit-hash: d6d711dd8f7ad5885294b8e1f0009a23dc1f8b1f
commit-date: 2017-10-10
host: x86_64-unknown-linux-gnu
release: 1.22.0-nightly
LLVM version: 4.0

يوجد قسم الكود ذي الصلة الذي يشتكي منه الصدأ هنا: https://github.com/fschutt/printpdf/blob/master/src/types/plugins/graphics/two_dimensional/image.rs#L29 -L39 و https: // github .com / fschutt / printpdf / blob / master / src / types / plugins / graphics / xobject.rs # L170 -L200 - تم تجميعها بشكل جيد قبل بضعة أسابيع ، وهذا هو السبب في أن المكتبة لا تزال تحمل شارة "build pass".

ومع ذلك ، في أحدث إصدار ليليًا ، يبدو أن TryFrom قد تعطل:

error[E0119]: conflicting implementations of trait `std::convert::TryFrom<_>` for type `types::plugins::graphics::two_dimensional::image::Image`:
  --> src/types/plugins/graphics/two_dimensional/image.rs:29:1
   |
29 | / impl<T: ImageDecoder> TryFrom<T> for Image {
30 | |     type Error = image::ImageError;
31 | |     fn try_from(image: T)
32 | |     -> std::result::Result<Self, Self::Error>
...  |
38 | |     }
39 | | }
   | |_^
   |
   = note: conflicting implementation in crate `core`

error[E0119]: conflicting implementations of trait `std::convert::TryFrom<_>` for type `types::plugins::graphics::xobject::ImageXObject`:
   --> src/types/plugins/graphics/xobject.rs:170:1
    |
170 | / impl<T: image::ImageDecoder> TryFrom<T> for ImageXObject {
171 | |     type Error = image::ImageError;
172 | |     fn try_from(mut image: T)
173 | |     -> std::result::Result<Self, Self::Error>
...   |
199 | |     }
200 | | }
    | |_^
    |
    = note: conflicting implementation in crate `core`

error: aborting due to 2 previous errors

error: Could not compile `printpdf`.

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

fschutt من المحتمل أن يكون الضمير المتعارض هو impl<T, U> TryFrom<T> for U where U: From<T> ، تمت إضافته في https://github.com/rust-lang/rust/pull/44174. قد يكون هناك T مثل T: ImageDecoder و Image: From<T> .

هل لا يزال هناك أي شيء لا يزال مطلوبًا لمغادرة البوابة المميزة؟

إذا تم تثبيت https://github.com/rust-lang/rust/issues/35121 أولاً ، فيمكننا إزالة النوع Infallible المقدم في https://github.com/rust-lang/rust/pull/ 44174 واستخدم ! بدلاً من ذلك. لا أعرف ما إذا كان هذا يعتبر شرطًا.

أعتقد أن المانع الرئيسي هنا لا يزال هو أنواع الأعداد الصحيحة. إما أن نستخدم سمة Cast منفصلة لأنواع الأعداد الصحيحة https://github.com/rust-lang/rust/pull/42456#issuecomment -326159595 ، أو نجعل عملية النقل # 41619 تحدث أولاً.

لذلك اعتدت أن يكون لدي تعداد يطبق TryFrom لـ AsRef<str> ، لكن هذا توقف قبل بضعة أشهر. اعتقدت أنه كان خطأ تم إدخاله ليلاً والذي سيختفي مع مرور الوقت ، لكن يبدو أن هذا ليس هو الحال. هل لم يعد هذا النمط مدعومًا لـ TryFrom ؟

impl<S: AsRef<str>> TryFrom<S> for MyEnum {
    type Error = &'static str;

    fn try_from(string: S) -> Result<Self, Self::Error> {
        // Impl here
    }
}
...

ما هي الخيارات الأخرى المتاحة للتحويل من كل من &str و String ؟

kybishop هل ينفذ MyEnum FromStr ؟ قد يكون هذا هو مصدر الكسر الخاص بك.

nvzqz لا ، على الرغم من أنني أوصيت باستخدام TryFrom عبر Rust IRC لأنه حل أكثر عمومية. عمل TryFrom في البداية بشكل رائع حتى حدث تغيير فاصل في الليل قبل بضعة أشهر.

تحرير: هل تقصد أنه يجب علي التبديل إلى تنفيذ FromStr ، أم أنه إذا كان _did_ يشير FromStr ، فقد يتسبب ذلك في حدوث كسر؟

EDIT 2: هنا هو الضمني الكامل ، قصير إلى حد ما وبسيط لأولئك الفضوليين: https://gist.github.com/kybishop/2fa9e9d32728167bed5b1bc0b9becd97

kybishop هل هناك سبب معين تريد تطبيقه مقابل AsRef<str> بدلاً من &str ؟

sfackler كنت تحت الانطباع أنه يسمح بالتحويل من كل من &str و String ، على الرغم من أنني ما زلت مبتدئًا في Rust ، لذلك ربما أسيء فهم بالضبط كيف AsRef<str> تم استخدام &str ومعرفة ما إذا كان AsRef يسمح بشيء لا يسمح به &str .

kybishop مثل https://github.com/rust-lang/rust/issues/33417#issuecomment -335815206 ، وكما تقول رسالة خطأ المترجم ، هذا تعارض حقيقي مع الضمانة impl<T, U> std::convert::TryFrom<U> for T where T: std::convert::From<U> التي كانت تمت إضافته إلى libcore.

يمكن أن يكون هناك نوع T (ربما في صندوق متجه نحو المصب) يقوم بتنفيذ كليهماFrom<MyEnum> و AsRef<str>ولديه MyEnum: From<T> . في هذه الحالة ، سيتم تطبيق كلا الضميرين ، لذلك لا يُسمح لكلا الضمنيين بالتواجد معًا.

تضمين التغريدة إذن ما هي الخيارات المتاحة للأشخاص الذين يرغبون في التحويل من كل من &str و &String ؟ هل يجب عليك فقط تضمين TryFrom لكليهما؟

تحرير: أعتقد أنه يمكنني فقط تناول العمل الإضافي والاتصال بـ .as_ref() على String s. يمكنني بعد ذلك الحصول على أداة ضمنية واحدة TryFrom مقابل str .

نعم ، يجب أن يعمل اثنان (ربما يكون أحدهما يعتمد على الآخر ، كما أشرت) طالما أن MyEnum لا ينفذ From<&str> أو From<String> .

ينفذkybishop String Deref<str> ، لذا فإن كتابة الاستدلال يجب أن تسمح لـ &String بالإكراه في &str عند تمريره إلى الضمانة TryFrom . قد لا يكون استدعاء as_ref مطلوبًا دائمًا.

إذا كان أي شخص يبحث عن مثال سريع لهذا: https://play.rust-lang.org/؟gist=bfc3de0696cbee0ed9640a3f60b33f5b&version=nightly

مع https://github.com/rust-lang/rust/pull/47630 على وشك تحقيق الاستقرار في ! ، هل هناك رغبة في العلاقات العامة لاستبدال Infallible بـ ! هنا ؟

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

type Infallible = !;

مجرد القفز. أنا مع scottmcm على ذلك.

ومع ذلك ، فإن هذا يضيف النفقات الإضافية التي تعتمدها هذه الميزة ( TryInto / TryFrom ) الآن على ميزة أخرى غير مستقرة - never_type .

أيضًا ، يتمتع Infallible بميزة أنه يوفر مزيدًا من المعلومات / الدلالي حول سبب عدم إمكانية إنشاء النوع. أنا أؤمن بنفسي الآن.

أرغب في أن يصبح ! كافيًا من نوع المفردات بحيث يُقرأ Result<_, !> حدسيًا على أنه "نتيجة معصومة" ، أو "نتيجة (حرفيًا) لا تخطئ أبدًا." يبدو لي استخدام اسم مستعار (بغض النظر عن نوع منفصل!) أمرًا زائدًا عن الحاجة ويمكنني أن أرى أنه يسبب لي على الأقل توقفًا مؤقتًا مؤقتًا عند قراءة توقيعات الكتابة - "انتظر ، كيف كان هذا مختلفًا عن ! مرة أخرى ؟ " YMMV ، بالطبع.

jdahlstrom أتفق تماما. سنحتاج إلى تقديم ذلك في كتاب Rust أو nomicon بحيث يكون "الحقيقة الأساسية" وودودًا.

لقد مر الآن عامان منذ تقديم RFC لواجهة برمجة التطبيقات هذه.

~ briansmith : هناك علاقات عامة استقرار جارية. ~

تحرير : أو ربما أنا متعب للغاية ...

لقد ربطت العلاقات العامة من نوع التثبيت ! .

منذ أن تم دمج العلاقات العامة للاستقرار ! للتو ، قمت بتقديم PR لاستبدال convert::Infallible بـ ! : # 49038

تم دمج https://github.com/rust-lang/rust/pull/49038 . أعتقد أن هذا كان آخر مانع للاستقرار ، يرجى إعلامي إذا فاتني مشكلة لم يتم حلها.

تضمينrfcbot fcp

rfcbot لا يستجيب لأنه تم بالفعل إكمال FCP آخر من قبل على https://github.com/rust-lang/rust/issues/33417#issuecomment -302817297.

عذرًا ، هناك بعض التلميحات التي يجب إعادة النظر فيها. الآن بعد أن أصبح لدينا impl<T, U> TryFrom<U> for T where T: From<U> ، يجب استبدال الضمانات المتبقية بقيمة TryFrom التي تحتوي على type Error = ! إما بضمانات From ، أو إزالتها ، أو جعلها غير معصومة عن الخطأ ( تغيير نوع الخطأ إلى نوع غير مأهول).

تلك التي يمكنني العثور عليها في هذه الحالة تتضمن usize أو isize . أفترض أن الضمانات From المقابلة غير موجودة لأن قابليتها للخطأ تعتمد على حجم المؤشر الهدف. في الواقع ، يتم إنشاء الضمانات TryFrom بشكل مختلف لأهداف مختلفة: https://github.com/rust-lang/rust/blob/1.24.1/src/libcore/num/mod.rs#L3103 -L3179 ​​هذا ربما يمثل خطرًا على قابلية النقل.

ولدت بشكل مختلف لأهداف مختلفة

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

استقرار العلاقات العامة: # 49305. بعد بعض المناقشة هناك ، يزيل هذا PR أيضًا بعض الدلائل الضمنية TryFrom التي تتضمن usize أو isize لأننا لم نقرر بين طريقتين مختلفتين لتنفيذهما. (ويمكننا بالطبع الحصول على واحدة فقط).

مشكلة تتبع مخصصة لأولئك: https://github.com/rust-lang/rust/issues/49415

عمل TryFrom بشكل مثالي على rustc 1.27.0-nightly (ac3c2288f 2018-04-18) بدون أي ميزة بوابات ، لكنه تعطل عند تجميعه باستخدام rustc 1.27.0-nightly (66363b288 2018-04-28) .

هل كان هناك أي تراجع في استقرار هذه الميزة في الأيام العشرة الماضية؟

kjetilkjeka هذه الميزة غير مستقرة مؤخرًا: # 50121.

(يُعاد الفتح منذ عودة الاستقرار)

kjetilkjeka تم إرجاع استقرار TryFrom في https://github.com/rust-lang/rust/pull/50121 جنبًا إلى جنب مع استقرار النوع ! ، بسبب TryFrom<U, Error=!> for T where T: From<U> ضمني. لم يتم استقرار النوع ! بسبب https://github.com/rust-lang/rust/issues/49593.

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

هل هناك سبب أساسي يمنعنا من المضي قدمًا في تثبيت السمة TryFrom نفسها ، وأي دلائل لا تتضمن ! ، وتؤجل فقط وضعيات التثبيت التي تتضمن ! حتى بعد الاستقرار المحتمل لـ ! ؟

https://github.com/rust-lang/rust/pull/49305#issuecomment -376293243 يصنف تطبيقات السمات المختلفة الممكنة.

joshtriplett بقدر ما أفهم ، impl<T, U> TryFrom<T> for U where U: From<T> { type Err = !; } على وجه الخصوص ليس متوافقًا مع الإصدارات السابقة للإضافة إذا كان TryFrom مستقرًا بالفعل.

تضمين التغريدة
لماذا هذا غير متوافق مع الإصدارات السابقة؟

(وهل نحتاج حقًا إلى البطانية؟)

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

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

(وهل نحتاج حقًا إلى البطانية؟)

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

يمكننا دائمًا الانتظار حتى يتوفر التخصص لتوفير الضمانة الشاملة ، وتحقيق الاستقرار في التحويلات الرقمية المختلفة في غضون ذلك.

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

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

كلما استقر هذا: _يرجى_ إضافة TryFrom و TryInto إلى المقدمة.

SergioBenitez لقد فعلنا ذلك في Nightly لفترة من الوقت ، وللأسف كان تغييرًا مفاجئًا للصناديق التي تحدد سمة TryFrom أو TryInto (للاستخدام بينما std واحد غير مستقر) ، يكفي أننا قمنا بإعادته.

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

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

تم تنفيذ حل عام لمشكلة مماثلة للتمديد
الطرق في https://github.com/rust-lang/rust/issues/48919 ، حيث تكون غير مستقرة
تصدر طرق الامتداد تحذيرات عند اصطدام كود ثابت.

يبدو أنه يمكنك فعل شيء مشابه بإضافة مصطلحات جديدة إلى
مقدمة؟

في الخميس ، 7 يونيو 2018 ، 11:47 صباحًا كتب Simon Sapin [email protected] :

SergioBenitez https://github.com/SergioBenitez لقد فعلنا ذلك
ليلا لفترة من الوقت ، وللأسف كان تغيير الصناديق
التي تحدد سمة TryFrom أو TryInto الخاصة بهم (للاستخدام أثناء الإصابة بالأمراض المنقولة جنسياً
تلك غير مستقرة) ، يكفي أننا عدناها.

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

ومع ذلك ، بالنسبة لـ TryFrom و TryInto ، سيكون الحل هو الحصول على ملف
مقدمة لنسخة 2018. وقد نوقش هذا بشكل غير رسمي من قبل ولكن
لم أجدها مرفوعة ، لذلك فتحت # 51418
https://github.com/rust-lang/rust/issues/51418 .

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

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

SimonSapin ما هي الخطة الحالية لتثبيت TryFrom / TryInto؟ لم أجد أي شيء ملموس بعد أن تمت إعادته في 29 أبريل.

nayato تم حظره عند الاستقرار (مرة أخرى) من النوع الذي لا يكتب أبدًا https://github.com/rust-lang/rust/issues/35121 ، والذي تم حظره بنفسه على https://github.com/rust-lang/rust/ القضايا / 49593.

SimonSapin يتم استخدام النوع أبدًا هنا فقط. هل يمكننا تحقيق الاستقرار في TryFrom بدون هذا التنفيذ وهبوطه عندما لا يكون مستقرًا أبدًا؟ TryFrom هي واجهة مهمة جدًا حتى بصرف النظر عن المكافئ بـ Try .

للأسف لا. سيكون تغييرًا مفاجئًا لإضافة هذا الضمني الشامل بعد استقرار السمة وإتاحة الفرصة لمكتبات crates.io للتنفيذ على سبيل المثال TryFrom<Foo> for Bar بينما لديهم أيضًا From<Foo> for Bar ، كما هو واضح. سوف تتداخل.

كان القرار حتى الآن هو أن جعل TryFrom "متوافقًا" بهذه الطريقة مع From كان مهمًا بدرجة كافية لمنع الاستقرار.

ربما فكرت ساذجة وسخيفة:

ماذا لو حصل rustc على خطأ دائم التشغيل في هذه الأثناء والذي تم تشغيله مقابل Error<_, !> ، مما يمنع أي إشارات ضمنية في userland ، مما يسمح بإضافة impl s لاحقًا؟

أو impl<T: From<U>, U> TryFrom<U> for T غير مستقر مع أي نوع خطأ. هل يمكن أن تكون السمات ضمنية غير مستقرة؟

jethrogb لا

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

تم حظره بسبب هذا الضمني:

impl<T, U> TryFrom<U> for T where T: From<U> {
    type Error = !;

    fn try_from(value: U) -> Result<Self, Self::Error> {
        Ok(T::from(value))
    }
}

@ rust-lang / libs ما رأيك في العودة إلى enum Infallible {} بدلاً من عدم الكتابة مطلقًا ، لإلغاء الحظر؟

أنا شخصياً لا أمانع في الحصول enum Infalliable {} ثم تغييره لاحقًا إلى type Infalliable = ! .

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

ما تذكرناه هو أننا سنقوم باستبدال واحد فقط: إذا أصبح اثنان من خام المكتبات الفارغة القياسية الأكثر استقرارًا (أنواع منفصلة) من نفس النوع ، فإن الصندوق الذي يحتوي على بعض impl s لكليهما سوف ينكسر حيث تصبح الضمانات متداخلة (أو متطابقة). على سبيل المثال ، impl From<std::convert::Invallible> for MyError و impl From<std::string::ParseError> for MyError .

بالمناسبة ، لدينا std::string::ParseError وهو على حد علمي الرقم الفارغ الوحيد في المكتبة القياسية 1.30.0. لذلك إذا كنا واثقين من عدم وجود مشكلة أخرى في هذه الخطة ، فيمكننا:

  • انقل string::ParseError إلى convert::Infallible
  • أعد تصديره في موقعه القديم باستخدام pub use أو pub type (هل هذا يحدث فرقًا؟)
  • استخدمه في ضمانة البطانية TryFrom
  • لاحقًا ، في نفس الإصدار عند استقرار النوع مطلقًا ، استبدل كلا من string::ParseError و convert::Infallible بـ type _ = ! واستخدم ! مباشرة حيث تم استخدامهم.
  • (اختياري) لاحقًا ، أرسل تحذيرات الإيقاف للأسماء المستعارة القديمة

بعد أن أضفت فقط وعلى مضض سمة العنصر النائب TryFrom إلى الصندوق الخاص بي ، ومن المسلم به دون الفهم الكامل للآثار المترتبة على RFC وجهود التثبيت ، فأنا مندهش من أن غطاء TryFrom مقابل From ما الذي يعيق هذا Infallible / ! ؟ أليست هذه الأهداف الثانوية ، بعد إنشاء std TryFrom ومستقر $ TryInto السمات؟ أعني ، حتى مع الافتقار إلى الارتفاع من From إلى TryFrom (الغرض الذي لا أفهمه تمامًا) ، لن يكون الأمر أقل إزعاجًا إذا لم تتم إضافة كل صندوق يحتاج إليه الخاصة بها TryFrom ؟

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

على الرغم من التفكير في الأمر ، ربما لن يكون تغييرًا قطعيًا مع التخصص؟

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

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

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

ما هو ضروري للخطة الموضحة في https://github.com/rust-lang/rust/issues/33417#issuecomment -423073898 لبدء الحدوث؟ ما الذي يمكن عمله للمساعدة؟

إنها مهمة غير مجدية إلى حد ما ، لكنها ستساعد إذا تمكن شخص ما من معالجة مشكلة التتبع هذه و https://github.com/rust-lang/rust/issues/35121 للتحقق مما إذا كانت هناك مشكلة في تلك الخطة التي نحن ' ناقشنا من قبل ونسينا منذ ذلك الحين ، على وجه الخصوص ما إذا كان استبدال enum Infallible بـ type Infallible = !; بعد استقرار التعداد في إصدار سابق يمكن أن يكون تغييرًا مفاجئًا.

هل يحتاج تعداد Infallible إلى الاستقرار جنبًا إلى جنب مع السمة؟ إذا ظل غير مستقر ، فلا يمكن لأحد تسميته ، وبالتالي فإن استبداله بـ ! لاحقًا يجب أن يكون جيدًا؟

seanmonstar لا ، يمكنك الرجوع إليه باستخدام <u16 as TryFrom<u8>>::Error ويعتبر اسمًا مستقرًا. شاهد:

// src/lib.rs
#![feature(staged_api)]
#![stable(since = "1.0.0", feature = "a")]

#[stable(since = "1.0.0", feature = "a")]
pub trait T1 {
    #[stable(since = "1.0.0", feature = "a")]
    type A;
}

#[unstable(issue = "12345", feature = "b")]
pub struct E;

#[stable(since = "1.0.0", feature = "a")]
impl T1 for u8 {
    type A = E;
}
// src/bin/b.rs
extern crate a;

trait T3 {}

impl T3 for <u8 as a::T1>::A {}
impl T3 for a::E {}

fn main() {}

لا يتسبب الضمني الأول T3 في حدوث أي خطأ. يتسبب الضمني T3 الثاني فقط في حدوث خطأ E0658 "استخدام ميزة مكتبة غير مستقرة".

هذا .... رائع ، فقط أطلب أن يلدغ> _ <

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

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

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

على سبيل المثال ، باستخدام String::from_utf8 ، يمكننا أن نرى أن نوع الخطأ يحتوي على الإدخال المملوك Vec<u8> حتى نتمكن من إعادته :

// some invalid bytes, in a vector
let sparkle_heart = vec![0, 159, 146, 150];

match String::from_utf8(sparkle_heart) {
    Ok(string) => {
        // owned String binding in this scope
        let _: String = string;
    },
    Err(err) => {
        let vec: Vec<u8> = err.into_bytes(); // we got the owned vec back !
        assert_eq!(vec, vec![0, 159, 146, 150]);
    },
};

لذا ، إذا حصلنا على تطبيق String: TryFrom<Vec<u8>> ، فمن المتوقع أن يكون <String as TryFrom<Vec<u8>>>::Error FromUtf8Error أليس كذلك؟

نعم ، يعد إعطاء قيمة الإدخال "back" في نوع الخطأ خيارًا صالحًا. أخذ مرجع بـ impl<'a> TryFrom<&'a Foo> for Bar هو أمر آخر.

في هذا المثال المحدد ، لست متأكدًا من أن الأداة الضمنية TryFrom مناسبة. UTF-8 هو واحد فقط من عمليات فك التشفير الممكنة للبايت في Unicode ، و from_utf8 يعكس ذلك في اسمه.

إنها مهمة جديرة بالخير ، ولكن من المفيد أن يمر شخص ما بمشكلة التتبع هذه و # 35121 للتحقق مما إذا كانت هناك مشكلة في تلك الخطة التي ناقشناها من قبل ونسيناها منذ ذلك الحين ، ولا سيما ما إذا كان سيتم استبدال enum Infallible مع type Infallible = !; بعد استقرار التعداد في إصدار سابق يمكن أن يكون تغييرًا مفاجئًا.

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

المفهوم الأصلي
مصدر قلق واحد
المضي قدما من فريق lib

تليها:

44174 29 سبتمبر: تمت إضافة نوع Infallible

47630 14 آذار (مارس): استقر على النوع أبدًا!

49038 22 مارس: تحول معصوم إلى من لا يكتب أبدًا!

49305 27 مارس: استقرت TryFrom

49518 30 مارس: تمت إزالته من المقدمة

50121 21 أبريل: غير مستقر

في ملاحظة البطانية تشير إلى عندما يستقر ! ،

هل سيعمل هذا؟

impl<T, U> TryFrom<U> for T
where U: Into<T> {
    type Err = !;

    fn try_from(u: U) -> Result<Self, !> { Ok(u.into()) }
}

impl<T, U> TryInto<U> for T
where U: TryFrom<T> {
    type Err = U::Err;

    fn try_into(self) -> Result<U, !> { U::try_from(self) }
}

بهذه الطريقة ، تحصل جميع التحويلات المعصومة (مع From و Into ) على تضمين ضمني مقابل TryFrom و TryInto حيث Err هو ! وحصلنا على TryInto لكل ضمانة TryFrom كما نحصل على Into لكل ضمانة From .

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

ثم يمكننا التحقق من impl TryFrom<T, Err = !> أو impl TryInto<T, Err = !> مع تلميح لاستخدام impl From<T> أو impl Into<T> .

لدينا بالفعل كلا من هذه الضمانات:

https://doc.rust-lang.org/1.31.0/std/convert/trait.TryFrom.html#impl -TryFrom٪ 3CU٪ 3E
https://doc.rust-lang.org/1.31.0/std/convert/trait.TryInto.html#impl -TryInto٪ 3CU٪ 3E

آه ، لم أراهم (هناك الكثير من الأدوات الأخرى المزدحمة بالمستندات). هل يمكننا التغيير

impl<T, U> TryFrom<U> for T where    T: From<U>,

ل

impl<T, U> TryFrom<U> for T where    U: Into<T>,

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

هل ترغب في تجربته ومعرفة ما إذا كان يتم تجميعه وإرسال طلب سحب؟

نعم أود المحاولة.

هل هناك حالة لا يمكن فيها تنفيذ From<U> for T ، ولكن يمكن تنفيذ Into<T> for U و TryFrom<U> for T ؟

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

use other_crate::OtherType;

struct MyType;

// this impl will fail to compile
impl From<MyType> for OtherType {
    fn from(my_type: MyType) -> OtherType {
        // impl details that don't matter
    }
}

// this impl will not fail to compile
impl Into<OtherType> for MyType {
    fn into(self) -> OtherType {
        // impl details that don't matter
    }
}

هذا بسبب قواعد اليتيم.
TryFrom و From هي نفسها من حيث القواعد اليتيمة ، وبالمثل TryInto و Into هي نفسها من حيث القواعد اليتيمة

KrishnaSannasi سيجمع مثالك ، ما عليك سوى إلقاء نظرة على مثال الملعب هذا

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

struct MyType<T>(T);

impl<T> From<MyType<T>> for (T,) {
    fn from(my_type: MyType<T>) -> Self {
        unimplemented!()
    }
}

impl<T> Into<(T,)> for MyType<T> {
    fn into(self) -> (T,) {
        unimplemented!()
    }
}

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


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

أنا أسأل أكثر عما إذا كانت هناك أي حالة تريد فيها اشتقاق TryFrom<U> for T تلقائيًا من Into<T> for U ، ولكن لا يمكن تنفيذ From<U> for T . يبدو لي غير منطقي.

أنا أتفهم بالتأكيد الرغبة في ضمّن بطانية Into إلى TryInto ، لكن للأسف لا يسمح نظام النوع حاليًا بمثل هذا الضمني الشامل لأنه يتعارض مع From إلى TryFrom يشير From إلى Into (أو على الأقل هذا ما أفهمه).

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

scottjmaddox لا أريد بالضرورة أن يشير $ Into إلى TryFrom بقدر ما أريد أن يشير $ Into إلى TryInto . أيضًا From و Into مكافئتان لغويًا ، فقط لأننا لا نستطيع التعبير عن ذلك في نظام السمات لدينا لا يعني ذلك

يتم اشتقاق TryFrom<U> for T تلقائيًا من Into<T> for U ، لكن لا يمكن تنفيذ where From<U> for T .

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

clarcharr حتى لو كان هذا هو الحال ، فلا تزال هناك طريقة للحصول على كل من Into يشير TryInto و TryFrom يشير TryInto مباشرة ، حيث أن البطانيتين من شأنه أن ينبثق الصراع. أود أن يشير Into إلى TryInto ، و TryFrom يشير إلى TryInto . الطريقة التي أقترحها ستفعل ذلك ، وإن كان بشكل غير مباشر.


لقد قدمت تعليقًا على طلب السحب حيث أشرت هنا الأسباب الرئيسية لهذا التغيير


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

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

  • From يعني Into (للاستقرار)
  • يشير $ TryFrom إلى تضمين TryInto أو TryInto TryFrom (لأنها متكافئة لغويًا)
  • يعني $ From TryFrom
  • يعني $ Into TryInto

لا يهمني إذا تم إجراؤها بشكل مباشر أو غير مباشر.

أو يمكننا جعل TryInto اسمًا مستعارًا:

trait TryInto<T> = where T: TryFrom<Self>;

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

أين سيتم تحديد طريقة into ؟

clarcharr مثل SimonSapin قال ، أين سيتم تعريف طريقة to. ليس لدينا أسماء مستعارة للسمات. يجب أن يكون ذلك RFC آخر. وسنحتاج إلى حظر هذا على ذلك RFC ، وهو أمر غير مرغوب فيه.

KrishnaSannasi من المستحيل حاليًا أن يكون لديك الأربعة From => Into ، From => TryFrom ، TryFrom => TryInto ، و Into => TryInto ، لأن كل ما ينفذ From سيفعل لديك اثنين من الضمانات المتنافسة مقابل TryInto (أحدهما من From => Into => TryInto والآخر من From => TryFrom => TryInto ).

نظرًا لأن From => Into و TryFrom => TryInto كلاهما حاسمان ، يجب التضحية بـ Into => TryInto . أو على الأقل هذا ما أفهمه.

نعم ، لم أكن أعتقد أن الاسم المستعار TryInto قد تم إخراجه بشكل صحيح لذا تجاهلني فقط> <

أنا أتفق مع ما يقوله scottjmaddox بالرغم من ذلك.

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

فقط لتوضيح سوء فهم محتمل ، أقوم بإزالة From auto impls TryFrom واستبداله بـ Into auto ب TryFrom .

من المستحيل حاليًا أن يكون لديك الأربعة من => إلى ، ومن => TryFrom ، و TryFrom => TryInto ، و Into => TryInto

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

هذا:
From -> Into -> TryFrom -> TryInto

يستخدم $ From تلقائيًا Into
يستخدم $ Into تلقائيًا TryFrom
يستخدم $ TryFrom تلقائيًا TryInto

بالتمديد ، بسبب الضمانات التلقائية المتعدية التي نحصل عليها
يشير $ From إلى TryFrom (لأن From يقوم تلقائيًا بتضمين Into و Into تلقائيًّا TryFrom )
يشير $ Into إلى TryInto (لأن Into يقوم تلقائيًا بتضمين TryFrom و TryFrom TryInto )
كل مستوى واحد من المراوغة (أعتقد أن هذا جيد)

لذلك تم استيفاء جميع شروطي.
يمكننا الحصول على كل من

ضمني | مستويات المراوغة
----------------------- | -------------------
From يعني Into | لا مراوغة
TryFrom يعني TryInto | لا مراوغة
From يعني TryFrom | 1 مستوى المراوغة
Into يعني TryInto | 1 مستوى المراوغة


From -> Into -> TryInto -> TryFrom
سأعمل أيضًا ، لكني أرغب في الحفاظ على الاتساق والإصدار الأصلي (كما هو موضح أعلاه) أكثر اتساقًا من هذا الإصدار


ملاحظة حول المصطلحات: (عند استخدامها)

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


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

طلب سحب

KrishnaSannasi Ahhh ، الآن أرى وجهة نظرك. نعم ، هذا منطقي وأرى الآن كيف يحل التغيير المقترح المشكلة ويوفر السلوك المطلوب. شكرا على الشرح الشامل!

تحرير : حسنًا ، انتظر ، رغم ذلك ... ما زلت لا أفهم سبب عدم كفاية الضمانات الشاملة الحالية. من المفترض أن تكون هناك حالة يمكنك فيها تنفيذ Into ولكن ليس From ؟ ومع ذلك فمن الممكن اشتقاق TryFrom تلقائيًا؟

تحرير 2 : حسنًا ، لقد عدت للتو وقرأت شرحك حول كيف يمكن أن تمنع القاعدة اليتيم تنفيذ From . كل ذلك من المنطقي الآن. أنا أؤيد تماما هذا التغيير. لا يزال من المحبط بعض الشيء أن القاعدة اليتيمة لها الكثير من العواقب غير المقصودة ، لكننا لن نصلح ذلك هنا ، لذلك من المنطقي تحقيق أفضل الأشياء.

هل سيكون شخص ما من @ rust-lang / libs على استعداد لبدء FCP على هذا الآن بعد أن تم إصلاح https://github.com/rust-lang/rust/issues/49593 و never_type مرشح لتحقيق الاستقرار مرة اخري؟

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

بعد وصول العلاقات العامة الخاصة بالتثبيت للنوع مطلقًا ، يمكننا إنشاء علاقات عامة (جديدة) لتحقيق الاستقرار مقابل TryFrom / TryInto واستخدام rfcbot هناك للتأكد من رؤية أعضاء الفريق له.

هل من الممكن تغيير أنواع التعداد الفارغة الحالية مثل FromStringError لتكون أسماء مستعارة إلى ! بجانب التثبيت؟

clarcharr نعم. بسبب التماسك الضمني للسمات ، يمكننا القيام بذلك لنوع واحد فقط. لحسن الحظ ، لدينا فقط std::string::ParseError . (وهذا هو بالضبط سبب استخدامنا لها بدلاً من إضافة نوع جديد مقابل impl FromString for PathBuf في https://github.com/rust-lang/rust/pull/55148.)

ومع ذلك ، هذا لا علاقة له بـ TryFrom / TryInto . هذا موضوع لـ https://github.com/rust-lang/rust/issues/49691 / https://github.com/rust-lang/rust/issues/57012 ، حيث يجب أن يحدث في نفس دورة الإصدار كاستقرار ! .

SimonSapin هل سيكون من الممكن دمج طلب السحب الخاص بي (# 56796) قبل أن يستقر هذا؟

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

شكرا!

هل يمكن دمج هذا في مستقر؟ إنها تبعية لـ argdata لـ CloudABI.

mcandre نعم. هذا ينتظر حاليًا https://github.com/rust-lang/rust/issues/57012 ، والذي تم إلغاء حظره مؤخرًا. آمل أن TryFrom إلى Rust 1.33 أو 1.34.

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

تم دمج 56796. حتى نتمكن من التحقق من ذلك.

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

العمل على التوثيق. مطبات طريق ثانوية: أريد assert_eq!(some_value, std::num::TryFromIntError(())); . ومع ذلك ، نظرًا لأن TryFromIntError ليس به مُنشئ ولا حقول عامة ، لا يمكنني إنشاء مثيل له. هل من نصيحة عن كيفية التقدم؟ في الوقت الحالي ، أعمل فقط assert!(some_value_result.is_err()); .

عذرًا إذا كان هذا هو المكان الخطأ ، ولكن يبدو أن هناك خطأ في المستندات لـ TryFromIntError - يسرد impl Debug for TryFromIntError ، بينما في الواقع لا يوجد رمز له. ينشئ المترجم خطأ حاليًا عند محاولة فك نتيجة من استخدام TryFrom.

هل من المفترض أن يكون هناك ضمانة fmt::Debug على هذا؟

@ marco9999

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct TryFromIntError(());

يحتوي على سمة #[derive(Debug)] .

حسنًا ، هناك ... هل هناك شيء لا يعمل بشكل صحيح؟

error[E0599]: no method named `unwrap` found for type `std::result::Result<usize, <T as std::convert::TryInto<usize>>::Error>` in the current scope
  --> src\types\b8_memory_mapper.rs:67:51
   |
67 |         let address: usize = T::try_into(address).unwrap();
   |                                                   ^^^^^^
   |
   = note: the method `unwrap` exists but the following trait bounds were not satisfied:
           `<T as std::convert::TryInto<usize>>::Error : std::fmt::Debug`

@ marco9999 ربما تفتقد قيدًا عامًا. يتم استخدام TryFromIntError بواسطة بعض الأنواع فقط ، لكن T الخاص بك يمكن أن يكون أي شيء:

fn foo<T: TryInto<u8>>(x: T) -> u8
where
    <T as TryInto<u8>>::Error: Debug,
{
    x.try_into().unwrap()
}

على أي حال ، هذا قليلاً خارج الموضوع ، آسف للجميع. قد يكون IRC مكانًا أفضل لطرح هذه الأسئلة.

أريد أن assert_eq!(some_value, std::num::TryFromIntError(()));

icefoxen لا توجد قيمة مفيدة مرتبطة بـ TryFromIntError لذلك لا يبدو أن هذا التأكيد له قيمة كبيرة. إذا كان لديك Result<_, TryFromIntError> وكان Err ، فلا توجد قيمة أخرى يمكن أن تكون.

assert!(some_value_result.is_err());

يبدو هذا مناسبا لي.

الإحالة المرجعية: https://github.com/rust-lang/rust/pull/58302

شكراglaebhoerl.

نظرًا لإصلاح خطأ الحظر (https://github.com/rust-lang/rust/issues/49593) كنت آمل أن يتم تثبيت النوع مطلقًا قريبًا® https://github.com/rust-lang/ الصدأ / القضايا / 57012 وإلغاء حظر هذا. ومع ذلك ، فقد ظهرت مشكلة جديدة (https://github.com/rust-lang/rust/issues/57012#issuecomment-460740678) ، وليس لدينا أيضًا إجماع على مشكلة أخرى (https://github.com / rust-lang / rust / issue / 57012 # issuecomment-449098855).

لذلك في اجتماع libs الأسبوع الماضي طرحت الفكرة مرة أخرى ، أعتقد أن أول من اقترحه scottmcm في https://github.com/rust-lang/rust/issues/33417#issuecomment -299124605 ، لتحقيق الاستقرار TryFrom و TryInto بدون النوع مطلقًا ، وبدلاً من ذلك يكون لديك تعداد فارغ يمكن تحويله لاحقًا إلى اسم مستعار إلى ! .

في المرة الأخيرة التي ناقشنا فيها هذا (https://github.com/rust-lang/rust/issues/33417#issuecomment-423069246) ، لم نتمكن من تذكر سبب عدم قيامنا بذلك في المرة السابقة.

في الأسبوع الماضي ، ذكّرنا dtolnay بالمشكلة: قبل أن يصبح ! نوعًا كاملاً ، يمكن استخدامه بالفعل بدلاً من نوع الإرجاع للدالة للإشارة إلى أنها لا تعود أبدًا. يتضمن هذا أنواع مؤشرات الوظائف. لذلك ، بافتراض أن https://github.com/rust-lang/rust/pull/58302 يهبط في هذه الدورة ، سيكون الرمز مثل هذا صالحًا في Rust 1.34.0:

use std::convert::Infallible;
trait MyTrait {}
impl MyTrait for fn() -> ! {}
impl MyTrait for fn() -> Infallible {}

نظرًا لأن fn() -> ! و fn() -> Infallible نوعان مختلفان (مؤشر) ، فإن الضمنيين لا يتداخلان. ولكن إذا استبدلنا التعداد الفارغ باسم مستعار من النوع pub type Infallible = !; (عندما يصبح ! نوعًا كاملاً) ، فحينئذٍ سيبدأ التضمين في التداخل وسيتعطل الرمز كما هو مذكور أعلاه.

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

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

بناءً على هذه المناقشة ، قمت بإرسال https://github.com/rust-lang/rust/pull/58302 وهو الآن في فترة التعليق النهائية.

يجب أن يكون 58015 جاهزًا للمراجعة / الدمج الآن.

@ kennytm أليس من الممكن الإشارة إلى ! في المستقر بالفعل؟ على سبيل المثال ، ضع في اعتبارك ما يلي:

trait MyTrait {
    type Output;
}

impl<T> MyTrait for fn() -> T {
    type Output = T;
}

type Void = <fn() -> ! as MyTrait>::Output;

بعد القيام بذلك ، يشير Void إلى النوع ! .

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

كيف يمكنني المساعدة في التوثيق؟ :-)

أوه اعتقدت أن https://github.com/rust-lang/rust/pull/58015 قد هبط ، لكنه لم يحدث بعد ... دعونا نناقشه هناك.

هل يمكن أن يكون للسمة TryFrom طريقة للتحقق مما إذا كان يمكن تحويل الوسيطة دون استهلاكها؟

fn check(value: &T) -> bool

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

عفوًا ، كان يجب إغلاق هذا عن طريق https://github.com/rust-lang/rust/pull/58302. يغلق الآن.


@ o01eg الطريقة المعتادة لإجراء تحويل غير مستهلك هي تنفيذ على سبيل المثال TryFrom<&'_ Foo> بدلاً من TryFrom<Foo> .

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

لا ، نقوم بإغلاق مشكلات التتبع عند استقرار العلاقات العامة للميزة.

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

يتم إغلاق مشكلات التتبع بواسطة العلاقات العامة التي تعمل على استقرارها. اعتمادًا على دورة الإصدار ، قد يستغرق هذا ما يصل إلى 12 أسبوعًا قبل تحرير المستقر.

فهمتك. شكرا للتوضيح للجميع! :)

gregdegruy ، قم بتحديث إصدار Rust الخاص بك إلى 1.34 أو أعلى.

هل كانت هذه الصفحة مفيدة؟
0 / 5 - 0 التقييمات