مشكلة في التتبع لـ https://github.com/rust-lang/rfcs/pull/1542
لكى يفعل:
TryFrom
blanket لاستخدام Into
بدلاً من From
هل هناك طريقة لطباعة خطأ بشكل عام بالقيمة الأصلية إذا فشل التحويل دون الحاجة إلى Clone
لذا فإن الطريقة ذات الصلة التي تثير الذعر عند الفشل قد تحتوي على رسائل خطأ لطيفة؟
مناقشة ما إذا كان هذا يجب أن يذهب في المقدمة معلقة عندما يصبح هذا مستقرًا .
نعتذر إذا تمت تغطية هذا في مكان آخر ، ولكن ما الذي نود رؤيته قبل وضع علامة عليه على أنه مستقر؟ أنا متأكد من أنني أعدت تطبيق هذه الوظيفة عدة مرات في مشاريع مختلفة ، لذا فإن السمة الشائعة القابلة لإعادة الاستخدام ستجعلني 😄
يمكننا طرحه للمناقشة في الدورة القادمة.
🔔 تدخل هذه المشكلة الآن في فترة تعليق نهائية لمدة دورة من أجل الاستقرار 🔔
كنقطة استقرار ، يود فريق libs أيضًا إضافة هذه السمات إلى المقدمة كجزء من استقرارها. سيتطلب ذلك أن يكون تشغيل فوهة البركان نظيفًا بنسبة 100٪ _ كحد أدنى_ ، لكننا واثقون نسبيًا من أن الحالة الحالية للحل تجعله متوافقًا مع الإصدارات السابقة لإضافة سمات إلى المقدمة.
لدي بعض الأسئلة حول الغرض من هذه السمات.
std
التي سيتم تنفيذها من أجلها؟impl TryFrom<T> for T
؟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
سوف:
TryFrom<str>
في التوقيع ، وهو موجود بشكل صحيح في وحدة التحويل ويكون أكثر وضوحًا ما يفعلهTryFrom<str>
لأنواع البيانات الخاصة بهم والحصول على str.parse::<YourSweetDataType>()
مجانًا ، إلى جانب try_from
الآخرون الذين يشعرون برغبة في التنفيذ ، مما يجعل تنظيم الكود المنطقي أفضل.أخيرًا ، بغض النظر عن الأفكار التجريدية ، إعادة استخدام الكود ، بلوخ بلا ، أعتقد أن إحدى الفوائد الأقل تقديرًا لسمات مثل هذه هي الشراء الدلالي الذي توفره للمبتدئين والمحاربين القدامى على حدٍ سواء. إنها توفر بشكل أساسي (أو بدأت في تقديم ، كلما زاد استقرارنا) مشهدًا موحدًا مع سلوك مألوف ومتعارف عليه. أمثلة رائعة حقًا على ذلك هي Default
، From
، Clone
. إنها توفر مشهدًا وظيفيًا لا يُنسى والذي يمكن للمستخدمين الوصول إليه عند إجراء عمليات معينة ، والذين يمتلكون بالفعل فهمًا جيدًا لسلوكهم ودلالاتهم (تعلم مرة واحدة ، يطبق في كل مكان). على سبيل المثال:
SomeType::default()
SomeType::from(other)
(يمكنك فقط كتابته ومعرفة ما إذا كان يتم تجميعه ، دون الوصول إلى الوثائق)clone()
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
ولكنه لا يزال يتطلب التحقق من الصحة.
إذا كنا سنغادر ضمنيًا
أعتقد أن المشكلة تكمن في أنه سيكون من الصعب تثبيت هذه الميزة بعد استقرارها وتوفير الأشخاص للوسائل اللازمة لكليهما.
أتوقع أن وجود 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 دمج هذا. الخطوة التالية هي المراجعة من قبل بقية الفرق الموسومة:
لا مخاوف مدرجة حاليا.
بمجرد توصل هؤلاء المراجعين إلى إجماع ، سيدخل هذا فترة التعليق النهائية. إذا اكتشفت مشكلة كبيرة لم تُثار في أي وقت في هذه العملية ، فيرجى التحدث!
راجع هذا المستند للحصول على معلومات حول الأوامر التي يمكن أن يقدمها لي أعضاء الفريق.
sfackler : فقط لتسجيل الوصول ، هل نحن جيدون بشأن مخاوف مختلفة حول !
والبطانية الضمنية؟ أعلم أننا تحدثنا عن هذا في اجتماع libs ، لكن سيكون من المفيد الحصول على ملخص هنا.
aturon كانت المناقشة الأخيرة حول ذلك sfackler تتساءل عما إذا كان يجب أن يوفر $ impl From<T> for U
impl TryFrom<T> for U
حيث TryFrom::Error = !
.
اقترح briansmith أن يتم اتخاذ قرار بشأن أن يكون RFC منفصلًا بمجرد طرح الأسئلة التي لم يتم حلها حول النوع مطلقًا.
أليست المشكلة الرئيسية في تحقيق الاستقرار الآن هي أن مثل هذا التغيير لا يمكن إجراؤه دون كسر التوافق مع الإصدارات السابقة؟ أم أن الحل هو عدم المضي قدمًا في هذا التغيير؟
أعتقد أن المجموعة الحالية من الضمانات لا يمكن الدفاع عنها. يمكنني أن أفهم أيًا من هذه المواقف:
TryFrom
هو إجراء تحويلات غير معصومة _ فقط_ ، لذلك لا تحتوي على أشياء مثل u8 -> u128
أو usize -> usize
.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
تليها:
في ملاحظة البطانية تشير إلى عندما يستقر !
،
هل سيعمل هذا؟
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.
لقد سئمت انتظار هذا ولدي وقت فراغ (أخيرًا). إذا كان هناك أي رمز أو أشياء من نوع التوثيق يمكنني القيام بها لدفع هذا إلى الأمام ، فأنا أتطوع للمساعدة.
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 وهو الآن في فترة التعليق النهائية.
@ 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 أو أعلى.
التعليق الأكثر فائدة
أرغب في أن يصبح
!
كافيًا من نوع المفردات بحيث يُقرأResult<_, !>
حدسيًا على أنه "نتيجة معصومة" ، أو "نتيجة (حرفيًا) لا تخطئ أبدًا." يبدو لي استخدام اسم مستعار (بغض النظر عن نوع منفصل!) أمرًا زائدًا عن الحاجة ويمكنني أن أرى أنه يسبب لي على الأقل توقفًا مؤقتًا مؤقتًا عند قراءة توقيعات الكتابة - "انتظر ، كيف كان هذا مختلفًا عن!
مرة أخرى ؟ " YMMV ، بالطبع.