Rust: مشكلة في تتبع ترقية ``! '' إلى نوع (RFC 1216)

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

مشكلة تتبع لـ rust-lang / rfcs # 1216 ، والتي ترفع ! إلى نوع.

القضايا المعلقة لحلها

أحداث وروابط مثيرة للاهتمام

A-typesystem B-RFC-approved B-unstable C-tracking-issue F-never_type Libs-Tracked T-lang T-libs finished-final-comment-period

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

petrochenkov انسى ! وانظر فقط إلى الأعداد.

إذا كان لدي تعداد بمتغيرين يمكنني مطابقته مع حالتين:

enum Foo {
    Flim,
    Flam,
}

let foo: Foo = ...;
match foo {
    Foo::Flim => ...,
    Foo::Flam => ...,
}

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

enum Void {
}

let void: Void = ...;
match void {
}

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

enum Foo {
    Flim,
    Flam,
}

let result_foo: Result<T, Foo> = ...;
match result_foo {
    Ok(t) => ...,
    Err(Flim) => ...,
    Err(Flam) => ...,
}

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

enum Void {
}

let result_void: Result<T, Void> = ...;
match result_void {
    Ok(t) => ...,
    // ERROR!
}

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

ال 259 كومينتر

حوزة!

يوجد تطبيق ويب لهذا هنا: https://github.com/canndrew/rust/tree/bang_type_coerced

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

خارطة الطريق الحالية الخاصة بي هي:

  • احصل عليه يعمل مع MIR. آمل ألا يكون هذا صعبًا للغاية لأن هذه هي الطريقة التي بدأت بها في تنفيذه ، لكنني كنت أواجه مشكلة حيث يبني MIR segfault أثناء التجميع.
  • قم بإزالة عناصر الاختلاف المتقادمة من المترجم ( FnOutput ، FnDiverging وما يتصل بها).
  • إخفاء النوع الجديد خلف بوابة الميزة. هذا يعني أنه عند تعطيل الميزة:

    • لا يمكن تحليل ! إلا كنوع في موضع الإرجاع.

    • متغيرات النوع المتشعب هي الافتراضية () .

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

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

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

eddyb ، @ arielb1 ، anyone_else : أفكار حول هذا النهج؟ لقد وصلت إلى هذه المرحلة إلى حد كبير (بدون عدة اختبارات فاشلة أحاول (ببطء شديد) إصلاحها).

ما هي السمات التي يجب أن نطبقها!؟ يتضمن PR # 35162 الأولي Ord وعدد قليل من الآخرين.

ألا يجب على ! تنفيذ _all_ السمات تلقائيًا؟

هذا النوع من الكود شائع إلى حد ما:

trait Baz { ... }

trait Foo {
    type Bar: Baz;

    fn do_something(&self) -> Self::Bar;
}

أتوقع أن يكون ! قابلاً للاستخدام مقابل Foo::Bar للإشارة إلى أن Bar لا يمكن أن يكون موجودًا على الإطلاق:

impl Foo for MyStruct {
    type Bar = !;
    fn do_something(&self) -> ! { panic!() }
}

لكن هذا ممكن فقط إذا نفذ ! كل السمات.

tomaka هناك RFC حول هذا الموضوع: https://github.com/rust-lang/rfcs/pull/1637

تكمن المشكلة في أنه إذا نفذ ! Trait ، فيجب أيضًا تطبيق !Trait ...

المشكلة هي أنه إذا! تنفذ السمات التي يجب أن تنفذها أيضًا! سمة ...

ثم حالة خاصة ! بحيث تتجاهل أي متطلبات سمة؟

tomaka ! لا يمكنه تنفيذ _all_ السمات تلقائيًا لأن السمات يمكن أن يكون لها طرق ثابتة وأنواع / ثوابت مرتبطة. يمكنه تلقائيًا تنفيذ السمات التي تحتوي فقط على طرق غير ثابتة على الرغم من (على سبيل المثال ، الطرق التي تتطلب Self ).

بالنسبة إلى !Trait ، اقترح أحدهم أن ! يمكنه التنفيذ التلقائي لكلٍّ من Trait _and_ !Trait . لست متأكدًا مما إذا كان هذا سليمًا ولكني أظن أن السمات السلبية ليست سليمة على الإطلاق.

لكن نعم ، قد يكون من الجيد أن يقوم ! بتنفيذ Baz تلقائيًا في المثال الذي قدمته لهذه الأنواع من الحالات على وجه التحديد.

متى بالضبط نختار متغيرات النوع المتشعب افتراضيًا إلى () / ! ومتى نخطئ حول عدم قدرتنا على استنتاج معلومات كافية من النوع؟ هل هذا محدد في أي مكان؟ أود أن أكون قادرًا على تجميع الكود التالي:

let Ok(x) = Ok("hello");

لكن الخطأ الأول الذي أحصل عليه هو " unable to infer enough type information about _ ". في هذه الحالة ، أعتقد أنه سيكون من المنطقي أن يكون _ افتراضيًا إلى ! . ومع ذلك ، عندما كنت أكتب اختبارات حول السلوك الافتراضي ، وجدت أنه من الصعب جدًا جعل متغير النوع افتراضيًا. هذا هو السبب في أن هذه الاختبارات معقدة للغاية.

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

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

هذه فكرة جيدة جدا في رأيي. نفس الشيء بالنسبة لـ None والذي سيكون افتراضيًا Option<!> على سبيل المثال.

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

اختبار unit_fallback هو بالتأكيد طريقة غريبة لتوضيح ذلك. نسخة أقل ماكرو ey هو

trait Balls: Default {}
impl Balls for () {}

struct Flah;

impl Flah {
    fn flah<T: Balls>(&self) -> T {
        Default::default()
    }
}

fn doit(cond: bool) {
    let _ = if cond {
        Flah.flah()
    } else {
        return
    };
}

fn main() {
    let _ = doit(true);
}

فقط متغير النوع الذي تم إنشاؤه بواسطة return / break / panic!() الافتراضي لأي شيء.

متى بالضبط نقوم افتراضيًا باستخدام متغيرات النوع المتشعب إلى () /! ومتى نخطئ بشأن عدم قدرتنا على استنتاج نوع كافٍ من المعلومات؟ هل هذا محدد في أي مكان؟

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

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

https://github.com/rust-lang/rust/issues/36011
https://github.com/rust-lang/rust/issues/36038
https://github.com/rust-lang/rust/issues/35940

هي بعض الأخطاء لإضافتها إلى قائمة القضايا المعلقة.

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

هل تم التخطيط لـ ! للتأثير على شمولية مطابقة الأنماط؟ مثال على السلوك الحالي الذي يحتمل أن يكون خاطئًا:

#![feature(never_type)]

fn main() {
    let result: Result<_, !> = Ok(1);
    match result {
//        ^^^^^^ pattern `Err(_)` not covered
        Ok(i) => println!("{}", i),
    }
}

tikue نعم ، إنه أحد الأخطاء المذكورة أعلاه.

lfairy whoops ، لم

هل هناك خطط لتنفيذ From<!> for T و Add<T> for ! (بنوع مخرجات ! )؟ أعلم أن هذا طلب محدد بشكل غريب حقًا - أحاول استخدام كليهما في هذا العلاقات العامة .

بالتأكيد From<!> for T . Add<T> for ! المحتمل أن يقرر فريق libs ! يجب أن ينفذ كل سمة لها تطبيق منطقي ومتعارف عليه.

@ canndrew شكرا! لقد اعتدت على سمة scala Nothing وهي نوع فرعي من كل نوع ، وبالتالي يمكن استخدامها إلى حد كبير في أي مكان يمكن أن تظهر فيه القيمة. ومع ذلك ، أنا بالتأكيد أتعاطف مع الرغبة في فهم التأثيرات التي قد تحدثها impl All for ! أو ما شابه ذلك على نظام نوع الصدأ ، خاصة فيما يتعلق بحدود السمات السلبية وما شابه ذلك.

لكل https://github.com/rust-lang/rfcs/issues/1723#issuecomment -241595070 From<!> for T به مشكلات تتعلق بالاتساق.

آه صحيح ، نعم. نحن بحاجة إلى القيام بشيء حيال ذلك.

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

impl<T> From<T> for T
    overridden_by<T> From<!> for T
{ ... }

impl<T> From<!> for T { ... }

ألا يشمل هذا التخصص؟ تحرير: أعتقد أن هذه هي القاعدة الضمنية للشبكة.

هل هو بديل عملي لتجنب الدعم الخاص للأنواع غير المأهولة قدر الإمكان؟

كل هذه match (res: Res<A, !>) { Ok(a) /* No Err */ } ، خاصة الطرق ل Result تبدو مفتعلا جدا، مثل الميزات من أجل الميزات، ولا يبدو أن يستحق كل هذا الجهد والتعقيد.
أفهم أن ! هي ميزة حيوان أليف canndrew ويريد تطويرها بشكل أكبر ، لكن ربما يكون هذا اتجاهًا خاطئًا للذهاب من البداية و # 12609 ليس حتى خطأ؟

petrochenkov # 12609 ليست ميزة خاصة للكتابة مطلقًا. إنه مجرد إصلاح للأخطاء لاكتشاف بعض الرموز التي يتعذر الوصول إليها بوضوح.

petrochenkov انسى ! وانظر فقط إلى الأعداد.

إذا كان لدي تعداد بمتغيرين يمكنني مطابقته مع حالتين:

enum Foo {
    Flim,
    Flam,
}

let foo: Foo = ...;
match foo {
    Foo::Flim => ...,
    Foo::Flam => ...,
}

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

enum Void {
}

let void: Void = ...;
match void {
}

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

enum Foo {
    Flim,
    Flam,
}

let result_foo: Result<T, Foo> = ...;
match result_foo {
    Ok(t) => ...,
    Err(Flim) => ...,
    Err(Flam) => ...,
}

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

enum Void {
}

let result_void: Result<T, Void> = ...;
match result_void {
    Ok(t) => ...,
    // ERROR!
}

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

petrochenkov ربما أساء فهم ما كنت تقوله. هناك سؤالان تمت مناقشتهما في الموضوع رقم 12609:

(0) هل ينبغي السماح لهذا الرمز بالتجميع؟

let res: Result<u32, !> = ...;
match res {
    Ok(x) => ...,
}

(1) هل ينبغي السماح لهذا الرمز بالتجميع؟

let res: Result<u32, !> = ...;
match res {
    Ok(x) => ...,
    Err(_) => ...,
}

كما هو مطبق حاليا ، فإن الإجابات هي "لا" و "نعم" على التوالي. # 12609 يتحدث عن (1) بالتحديد في القضية نفسها لكني كنت أفكر في (0) عندما أجبت. بالنسبة لما يجب أن تكون عليه الإجابات ، أعتقد أن (0) يجب أن تكون بالتأكيد "نعم" ولكن بالنسبة لـ (1) لست متأكدًا أيضًا.

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

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

بالمناسبة ، قمت بإجراء علاقات عامة لمحاولة الإصلاح (0) هنا: https://github.com/rust-lang/rust/pull/36476

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

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

أعتقد أنه من المفيد لوحدات الماكرو (1) ألا يكون خطأً صعبًا.

أعتقد أن (1) يجب أن يُجمِّع افتراضيًا ولكن يعطي تحذيرًا من نفس النسق الموجود هنا:

fn a() -> u32 {
    return 4;
    5
}
warning: unreachable expression, #[warn(unreachable_code)] on by default

أثناء وجودنا فيه ، هل من المنطقي جعل بعض الأنماط غير قابلة للدحض في مواجهة ! ؟

let res: Result<u32, !> = ...;
let Ok(value) = res;

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

لقد كان لدي بعض العلاقات العامة جالسين لبعض الوقت يتجمعون بالتعفن. هل هناك أي شيء يمكنني القيام به للمساعدة في مراجعة هذه؟ ربما هناك حاجة إلى بعض النقاش حولهم. أنا أتحدث عن # 36476 و # 36449 و # 36489.

أنا ضد فكرة التفكير في النوع المتشعب (أو النوع "السفلي" في نظرية النوع) هو نفس النوع الفارغ enum النوع (أو النوع "صفر" في نظرية النوع). هم مخلوقات مختلفة ، على الرغم من أنه لا يمكن أن يكون لكليهما أي قيمة أو مثال.

في رأيي ، يمكن أن يظهر النوع السفلي فقط في أي سياق يمثل نوع إرجاع. على سبيل المثال،

fn(A,B)->!
fn(A,fn(B,C)->!)->!

لكن لا يجب أن تقول

let g:! = panic!("whatever");

أو

fn(x:!) -> !{
     x
}

او حتى

type ID=fn(!)->!;

نظرًا لعدم وجود متغيرات من النوع ! ، وبالتالي لا يجب أن تحتوي متغيرات الإدخال على النوع ! .

فارغ enum مختلف في هذه الحالة ، يمكنك القول

enum Empty {}

impl Empty {
    fn new() -> Empty {
         panic!("empty");
    }
}

من ثم

 match Empty::new() {}

هذا يعني أن هناك فرقًا جوهريًا بين ! و Empty : لا يمكنك التصريح بأن أي متغير من النوع ! ولكن يمكنك القيام بذلك مقابل Empty .

earthengine ما هي ميزة تقديم هذا (في نظري مصطنع تمامًا) التمييز بين نوعين من الأنواع غير الصالحة للسكن؟

تم طرح العديد من الأسباب لعدم وجود مثل هذا التمييز - على سبيل المثال ، القدرة على كتابة Result<!, E> ، اجعل هذا يتفاعل بشكل جيد مع الوظائف المتباينة ، مع الاستمرار في استخدام العمليات الأحادية على Result ، مثل map و map_err .

في البرمجة الوظيفية ، يتم استخدام النوع الفارغ ( zero ) بشكل متكرر لتشفير حقيقة أن الوظيفة لا ترجع أو أن القيمة غير موجودة. عندما تقول bottom type ، فليس من الواضح لي أي مفهوم من نظرية النوع تشير إليه ؛ عادةً ما يكون bottom هو اسم النوع الذي يمثل نوعًا فرعيًا من جميع الأنواع - ولكن بهذا المعنى ، لا يكون أي من ! ليس رقمًا فارغًا هو bottom في Rust. ولكن مرة أخرى ، فإن zero ليس من النوع bottom ليس من غير المألوف في نظرية النوع.

هذا يعني أن هناك فرقًا جوهريًا بين ! و Empty : لا يمكنك التصريح عن أي متغير على أنه من النوع ! ولكن يمكنك القيام بذلك مقابل Empty .

هذا ما يقوم RFC بإصلاحه. ! ليس "نوعًا" حقًا إذا كنت لا تستطيع استخدامه كنوع.

تضمين التغريدة
هذه المفاهيم من المنطق الخطي https://en.wikipedia.org/wiki/Linear_logic

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

top هي قيمة يمكن استخدامها بأي طريقة ممكنة

ماذا يعني هذا في فرعي؟ أنه يمكن أن يصبح أي نوع؟ لأن هذا هو bottom إذن.

eddyb لقد ارتكبت بعض الأخطاء ، يرجى انتظار تحديثاتي الجديدة.

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

الرجال وما يليها

@ canndrew أرغ ، أنا آسف لذلك. = (كنت أخطط على r + 'في العلاقات العامة الخاصة بك اليوم أيضًا ...

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

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

باستخدام https://is.gd/4EC1Dk كمثال ونقطة مرجعية ، ماذا لو تجاوزنا ذلك ، و.

1) تعامل مع أي وظيفة يمكن أن تصيب بالذعر ولكنها لا تُرجع خطأ أو نتيجة ، اجعل نوع التوقيع يتغير ضمنيًا من -> Foo إلى -> Result<Foo,!> 2) any النتيجةtypes would have their Error types implicitly be converted to enum AnonMyErrWrapper {Die (!)، Error} ``
3) منذ! هو إعلان غير قابل للسكن بحجم صفري ، ولن يكون هناك أي تكلفة للتحويل بين الأنواع ، ويمكن إضافة تحويل ضمني لجعله متوافقًا مع الإصدارات السابقة.

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

أدرك أن هذا سيكون ... غير تافه من وجهة نظر المجتمع ، إن لم يكن من وجهة نظر تقنية. :)

أيضًا ، من المحتمل أن يتداخل هذا مع نظام التأثيرات المستقبلية المحتملة.

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

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

nikomatsakis فيما يتعلق بالقضايا المعلقة ، يمكن وضع علامة على هذين الاثنين

  • تنظيف الكود من # 35162 ، أعد تنظيم الكتابة قليلاً لأنواع الخيوط من خلال موضع الإرجاع بدلاً من استدعاء expr_ty
  • حل معالجة الأنواع غير المأهولة في المباريات

سأبدأ شرخًا آخر في هذا:

  • كيفية تنفيذ التحذيرات للأشخاص الذين يعتمدون على (): السمات الاحتياطية حيث قد يتغير هذا السلوك في المستقبل؟

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

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

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

حسنا عظيم!

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

ما أعنيه هو أنني أقوم بإضافة منطقي إلى TyTuple

TyTuple(&'tcx Slice<Ty<'tcx>>, bool),

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

نأمل أن نحتاج فقط إلى الاحتفاظ بهذا التنبيه لدورة تحذير واحدة.

فيما يتعلق بقضية uninitialized / transmute / MaybeUninitialized ، أعتقد أن الإجراء المعقول سيكون:

  • أضف MaybeUninitialized إلى المكتبة القياسية ، أقل من mem
  • أضف شيكًا على uninitialized أن نوعه معروف بأنه مأهول. رفع تحذير بخلاف ذلك بملاحظة تفيد بأن هذا سيكون خطأ فادحًا في المستقبل. اقترح استخدام MaybeUninitialized بدلاً من ذلك.
  • أضف شيكًا على transmute أن النوع "to" غير مأهول فقط إذا كان النوع "من" كذلك. وبالمثل ، قم بإصدار تحذير سيصبح خطأً فادحًا.

أفكار؟

انظر هذا الموضوع حول التغييرات الأخيرة على الشمولية:

يبدو أن هناك بعض الخلاف حول دلالات &! . بعد # 39151 ، مع تمكين البوابة المميزة never_type ، يتم التعامل معها على أنها غير مأهولة في المباريات.

إذا كانت السمة التلقائية تشير إلى أن ! لن تكون موجودة ، على الأقل تنفيذ السمات المناسبة من std سيكون مفيدًا للغاية. أحد القيود الضخمة على void::Void هو أنه يعيش خارج std وهذا يعني أن البطانية impl<T> From<Void> for T مستحيلة وهذا يحد من معالجة الأخطاء.

أعتقد أنه يجب تنفيذ From<!> على الأقل لجميع الأنواع و Error و Display و Debug يجب تنفيذها مقابل ! .

أعتقد أنه يجب تنفيذ From<!> على الأقل لجميع الأنواع

هذا يتعارض للأسف مع الضمنية From<T> for T .

هذا يتعارض للأسف مع الضمنية From<T> for T .

على الأقل حتى يتم تنفيذ التخصص الضمني الشبكي.

نقاط جيدة. اتمنى ان ارى ذلك يوما ما

هل يجب أن يكون النوع الفرعي [T; 0] النوع الفرعي [!; 0] و &[T] النوع الفرعي a &[!] ؟ يبدو بديهيًا بالنسبة لي أن الإجابة يجب أن تكون "نعم" ، لكن التنفيذ الحالي يعتقد عكس ذلك.

[!; 0] مأهولة وكذلك &[!] لذا أقول لا. علاوة على ذلك ، نحن لا نستخدم التصنيف الفرعي هذه المرة.

لا يجب أن يكون كلا النوعين [!; 0] و &[!] غير مسكونين ، يمكن لكلا النوعين قبول القيمة [] (أو &[] ).

لم يقل أحد أنهم غير مأهولة أو يجب أن يكونوا غير مأهولة.

let _: u32 = unsafe { mem::transmute(return) };
من حيث المبدأ ، قد يكون هذا جيدًا ، لكن المترجم يشكو من "التحويل بين 0 بت و 32 بت".

ومع ذلك ، يُسمح بتحويل ! -> () . ليس لدي أي سبب محدد للإشارة إلى ذلك ؛ إنه تناقض ، لكن لا يمكنني التفكير بمشكلة عملية أو نظرية في ذلك.

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

هل من الممكن تنفيذ Fn و FnMut و FnOnce لـ ! بطريقة عامة على جميع أنواع المدخلات والمخرجات؟

في حالتي ، لدي منشئ عام يمكنه أن يأخذ وظيفة ، والتي سيتم استدعاؤها عند البناء:

struct Builder<F: FnOnce(Input) -> Output> {
    func: Option<F>,
}

نظرًا لأن func هو Option ، فلا يمكن للمنشئ الذي يستخدم None استنتاج نوع F . لذلك يجب أن يستخدم التنفيذ fn new Bang:

impl Builder<!> {
    pub fn new() -> Builder<!> {
        Builder {
            func: None,
        }
    }
}

يجب أن تبدو وظيفة المنشئ func شيئًا كهذا ليتم استدعاؤها في كل من Builder<!> و Builder<F> :

impl<F: FnOnce(Input) -> Output> Builder<F> {
    pub fn func<F2: FnOnce(Input) -> Output>(self, func: F) -> Builder<F2> {
        Builder {
            func: func,
        }
    }
}

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

هذا يبدو متناقضا. ألا يحتاج <! as Fn>::Output إلى حل نوع واحد؟

<! as Fn>::Output هو ! ، أليس كذلك؟

نظريًا ، يجب أن يكون <! as Fn>::Output ! ، لكن مدقق النوع الحالي يريد الأنواع المرتبطة مطابقة تمامًا: https://is.gd/4Mkxfm

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

يجب أن الكود التالي

#![feature(never_type)]

fn with_print<T>(i:i32, r:T) -> T {
    println!("{}", i);
    r
}


fn main() {
    #[allow(unreachable_code)]
    *with_print(10,&return)
}

طباعة 10 ؟ في الوقت الحالي لا يطبع أي شيء. أو أي حلول أخرى ستؤخر return حتى الطباعة؟

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


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

على سبيل المثال ، سيكون من الجيد أن نكتب

#![feature(never_type)]

fn with_print<T>(i:i32, r:T) -> T {
    println!("{}", i);
    r
}


fn main() {
    for i in 1..10 {
        if i==5 { with_print(i, /* delay */break) }
        with_print(i, i);
    }
}

مع تأجيل الفاصل حتى طباعة 5 .

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

السبب في أن return نوع ! هو أنه يتباعد. لا يمكن أبدًا تنفيذ الكود الذي يستخدم نتيجة تعبير return لأن return "لا ينتهي" أبدًا.

oO لم أكن أعرف أنه يمكنك كتابة &return . هذا لا معنى له ، لماذا يجب أن يسمح لك بأخذ العنوان return ؟ ^^

ولكن ، على أي حال ، earthengine في التعليمات البرمجية الخاصة بك ، يتم تقييم الوسائط قبل استدعاء quit_with_print . يؤدي تقييم الوسيطة الثانية إلى تشغيل return ، مما يؤدي إلى إنهاء البرنامج. هذا مثل كتابة شيء مثل foo({ return; 2 }) - foo لا يتم تنفيذه أبدًا.

RalfJung return هو تعبير من النوع ! ، تمامًا مثل break أو continue . لها نوع ولكن نوعها غير مأهولة.

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

الشيء الوحيد الذي لم يتم حله هو ما يجب فعله بشأن العناصر الجوهرية التي يمكنها إرجاع ! (على سبيل المثال ، uninitialized ، ptr::read و transmute ). بالنسبة إلى uninitialized ، آخر ما سمعته كان هناك إجماع على أنه يجب إهمالها لصالح نوع MaybeUninit والمستقبل المحتمل &in ، &out ، &uninit مراجع. لا يوجد RFC لهذا ، على الرغم من وجود RFC سابق حيث اقترحت إهمال uninitialized فقط للأنواع التي لا تطبق سمة Inhabited . ربما يجب إلغاء طلب RFC واستبداله بـ "إهمال uninitialized " و "إضافة MaybeUninit " RFCs؟

بالنسبة إلى ptr::read ، أعتقد أنه من الجيد تركها كـ UB. عندما يتصل شخص ما بـ ptr::read فإنهم يؤكدون أن البيانات التي يقرؤونها صحيحة ، وفي حالة ! فهي ليست كذلك بالتأكيد. ربما يكون لدى شخص ما رأي أكثر دقة حول هذا بالرغم من ذلك؟

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

إذن أين نحن مع هؤلاء في الوقت الحالي؟ (/ cc nikomatsakis)؟ هل سنكون مستعدين للمضي قدمًا في إهمال uninitialized وإضافة نوع MaybeUninit ؟ إذا تسببنا في حالة الذعر الجوهرية هذه عند استدعائنا بـ ! ، فهل سيكون هذا مقياسًا مناسبًا لسد الفجوة يسمح لنا باستقرار ! ؟

هناك أيضًا المشكلات المعلقة المدرجة:

ما السمات التي يجب أن نطبقها مقابل ! ؟ يتضمن PR # 35162 الأولي Ord وعدد قليل آخر. من المحتمل أن تكون هذه مشكلة متعلقة بـ T-libs ، لذلك أقوم بإضافة هذه العلامة إلى المشكلة.

يوجد حاليًا تحديد أساسي إلى حد ما: PartialEq ، Eq ، PartialOrd ، Ord ، Debug ، Display ، Error . إلى جانب Clone ، والذي يجب إضافته بالتأكيد إلى تلك القائمة ، لا يمكنني رؤية أي شخص آخر له أهمية حيوية. هل نحن بحاجة إلى منع الاستقرار على هذا؟ يمكننا فقط إضافة المزيد من الضمانات لاحقًا إذا رأينا ذلك مناسبًا.

كيف يمكن تنفيذ التحذيرات للأشخاص الذين يعتمدون على (): Trait احتياطيًا حيث قد يتغير هذا السلوك في المستقبل؟

تم تنفيذ التحذير resolve_trait_on_defaulted_unit وهو ثابت بالفعل.

الدلالات المطلوبة لـ ! في الإكراه (# 40800)
أي نوع من المتغيرات يجب أن يتراجع إلى ! (# 40801)

هذه تبدو وكأنها حاصرات حقيقية. nikomatsakis : هل لديك أي أفكار محددة حول ما يجب فعله حيال هذه الأمور التي تنتظر تنفيذها؟

هل يعقل أن ينفذ ! أيضًا Sync و Send ؟ هناك العديد من الحالات التي تحتوي فيها أنواع الأخطاء على حدود Sync و / أو Send ، حيث سيكون من المفيد استخدام ! أنه "لا يمكن الفشل".

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

Kixunil ألن يكون أكثر صراحة استخدام https://doc.rust-lang.org/beta/std/intrinsics/fn.unreachable.html هنا؟

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

jsgf معذرة ، نعم ، ! أيضًا جميع سمات العلامة ( Send ، Sync ، Copy ، Sized ).

Kixunil همم ، هذا عار. سيكون من الأفضل كثيرًا تثبيت القيمة الجوهرية لـ unreachable .

ليس مانع استقرار (لأن perf ، وليس الدلالات) ، ولكن يبدو أن Result<T, !> لا يستخدم مساحة غير مأهولة ! في تخطيط التعداد أو مطابقة الترميز: https://github.com / رست لانج / صدأ / قضايا / 43278

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

هل يمكن أن تساعد الطباشير في الإجابة عن بعض الأسئلة الأكثر صعوبة فيما يتعلق بالتطبيقات !: Trait ؟

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

يوجد بالفعل resolve-trait-on-defaulted-unit نسالة في rustc. هل يجب وضع علامة على العنصر المقابل؟

canndrew هل من ! ؟

taralx هذا ليس غير سليم ، لكنه سيمنع رمز صالح من العمل. لاحظ أن الكود التالي يجمع اليوم:

trait Foo {
    type Bar;
    fn make_bar() -> Self::Bar;
}

impl Foo for ! {
    type Bar = (i32, bool);
    fn make_bar() -> Self::Bar { (42, true) }
}

lfairy يعتمد ذلك على رأيك في الرمز " ! .

earthengine لا أرى النقطة التي تحاول ! لا ، فلا علاقة له بذلك.

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

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

يجب أن يكون أي شيء نتعامل معه مساويًا أو أكثر تحفظًا مع ذلك (وهو ما لا يناسبه أي ضمني تلقائي).

@ Ericson2314 لا أفهم ماذا يعني ذلك ، هل يمكنك إعطاء بعض الأمثلة؟

SimonSapin تعني قاعدة "لا مزيد من الاختلاف" عدم وجود panic!() أو loop { } ، لكن وجود v: ! الموجود بالفعل في النطاق جيد. مع تعبيرات مثل تلك المستبعدة ، العديد من السمات لها ضمني واحد فقط ممكن! هذا النوع من الشيكات. (قد يكون لدى الآخرين عقد غير رسمي يستبعد كل شيء ما عدا 1 ، لكن لا يمكننا التعامل مع هؤلاء بشكل تلقائي.)

// two implementations: constant functions returning true and false.
// And infinitely more with side effects taken into account.
trait Foo { fn() -> bool }
// Exactly one implementation because the body is unreachable no matter what.
trait Bar { fn(Self) -> Self }

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

ما الذي يمنع هذا في الواقع من الاستقرار؟ هل هو الوضع mem::uninitialized أم شيء آخر؟

@ arielb1 : أعتقد أنه # 40800 و # 40801 أيضًا. هل تعرف ما هو الوضع على هؤلاء؟

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

هل هناك أي خطط للسماح بتحديد أن ASM متشعب حتى نتمكن من كتابة أشياء مثل؟

#[naked]
unsafe fn error() -> !{
  asm!("hlt");
}

دون الحاجة إلى استخدام loop{} لإرضاء مدقق النوع؟

تحرير: على الرغم من أنني أفترض أن استخدام core::intrinsics::unreachable قد يكون مقبولًا في مستوى منخفض جدًا من التعليمات البرمجية.

@ Eroc33 عندما يكون ! نوعًا ما ، يمكنك القيام بذلك:

#[naked]
unsafe fn error() -> ! {
  asm!("hlt");
  std::mem::uninitialized()
}

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

#[naked]
unsafe fn error() -> ! {
  asm!("hlt");

  enum Never {}
  let never: Never = std::mem::uninitialized();
  match never {}
}

Kixunil ! هو نوع في كل ليلة تحتاج إلى استخدام asm! . عادة ما يتم ذلك على النحو التالي:

#[naked]
unsafe fn error() -> ! {
    asm!("hlt");
    std::intrinsics::unreachable();
}

Kixunil يمكن القيام بذلك بسهولة أكبر باستخدام std::intrinics::unreachable() الذي يحدد بالضبط أن الكود السابق متباعد.

Kixunil من المناقشة الواردة أعلاه std::mem::uninitialized التمكن من العودة ! يظهر أن تكون خاضعة للتغيير على الرغم من؟ إلا إذا أسأت فهم ذلك؟

من المناقشة الواردة أعلاه std::mem::uninitialized التمكن من العودة ! يظهر أن تكون خاضعة للتغيير على الرغم من؟ إلا إذا أسأت فهم ذلك؟

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

أعتقد أيضًا أننا سنحتاج إلى تثبيت std::intrinsics::unreachable في مكان ما في libcore وتسميته unchecked_unreachable أو شيء ما لتمييزه عن الماكرو unreachable! . كنت أقصد كتابة RFC لذلك.

eddyb آه ، نعم ، نسيت أن asm!() غير مستقر ، لذلك يمكن استخدام std::intrinsics::unreachable() أيضًا.

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

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

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

أخيرًا ، أود أن أكرر أنني أوافق على أن intrinsics::unreachable() أفضل من الاختراق الحالي. ومع ذلك ، فأنا أعارض حظر أو حتى إيقاف هذه الاختراقات حتى يكون هناك بديل كافٍ.

استبدال غير مهيأ هو الاتحاد {!، T}. يمكنك الحصول على غير قابلة للوصول عن طريق
ptr :: read :: <* const!> () ing والعديد من الطرق المماثلة الأخرى.

في 8 آب (أغسطس) 2017 ، الساعة 3:58 مساءً ، إشعارات "مارتن هابوفشتياك" "Martin Habovštiak"
كتب:

eddyb https://github.com/eddyb آه ، نعم ، نسيت ذلك asm! () هو
غير مستقر ، لذلك يمكن استخدام () std :: intrinsics :: unreachable أيضًا.

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

@ Eroc33 https://github.com/eroc33 ما أفهمه هو أن بعض الناس
أقترح القيام بذلك ، لكنه مجرد اقتراح وليس قرارًا محددًا. و انا
الوقوف ضد مثل هذا الاقتراح لأنه قد يكسر التوافق مع الإصدارات السابقة ،
إجبار الصدأ ليصبح 2.0. لا أعتقد أن هناك أي ضرر يدعمها.
إذا كان الناس قلقين بشأن الحشرات ، فسيكون الوبر أكثر ملاءمة.

canndrew https://github.com/canndrew لماذا تعتقد أنه غير مهيأ
سيتم إهماله؟ لا أصدق مثل هذا الشيء. أجده مفيدًا للغاية ،
لذلك ما لم يكن هناك بديل جيد جدًا ، فلا أرى أي سبب لذلك
القيام بذلك.

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

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

nagisa شكرا! يبدو أنه سيحل المشكلة على المستوى الفني.

هل من الممكن اقتطاع مجموعة فرعية من هذا بحيث يمكن استخدام ! كمعامل نوع في أنواع الإرجاع؟ الآن إذا كان لديك وظيفة مستقرة

fn foo() -> ! { ··· }

وتريد استخدام ? ، فلا يمكنك إجراء التحويل المعتاد إلى

fn foo() -> io::Result<!> { ··· }

هل يؤثر الإكراه الشائك والأسئلة الافتراضية لمعلمات النوع في هذه الحالة؟

40801 يمكن وضع علامة عليه.

يجب أن نحاول إصلاح # 43061 قبل أن نستقر ! .

لم أشاهد أي مشكلات I-unsound مفتوحة تذكر نوع never_type ، لذا قدمت مشكلة جديدة لهذا: # 47563. يبدو أن الأمور تفترض أن [!; 0] غير مأهولة ولكن يمكنني إنشاء واحدة باستخدام [] بسيط.

تمت إضافة

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

nikomatsakis أنا متأكد من أنه يمكنني إيجاد بعض الوقت للعمل على هذه الأشياء. ما الذي يجب عمله بالضبط بالرغم من ذلك؟ هل هي فقط الخلل المرتبطة بهذه المشكلة؟

يبدو أن varkor لديه بالفعل

على الرغم من شيء آخر: هل نريد أن نجعل الذعر mem::uninitialized::<!>() في وقت التشغيل (إنه يسبب حاليًا UB)؟ أم يجب أن نترك هذه الأنواع من التغييرات في الوقت الحالي؟ لست على اطلاع دائم بما يحدث مع عناصر إرشادات التعليمات البرمجية غير الآمنة.

أعتقد أن الخطة لا تزال https://github.com/rust-lang/rfcs/pull/1892 ، والتي لاحظت للتو أنك كتبتها. :)

RalfJung هل هناك أي شيء على وجه الخصوص يمنع ذلك؟ يمكنني كتابة PR اليوم الذي يضيف MaybeUninit ويؤدي إلى إهمال uninitialized .

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

لقد قمت بإنشاء علاقات عامة لتحقيق الاستقرار في ! : https://github.com/rust-lang/rust/pull/47630. لا أعرف إذا كنا جاهزين لدمجها بعد. يجب أن ننتظر على الأقل لنرى ما إذا كان موظفو العلاقات العامةvarkor يصلحون المشكلات المفتوحة المتبقية.

أعتقد أيضًا أنه يجب علينا إعادة زيارة https://github.com/rust-lang/rfcs/pull/1699 حتى يتمكن الأشخاص من البدء في كتابة سمات أنيقة مقابل ! .

canndrew : على الرغم من عدم قبول طلب التعليقات هذا ، إلا أنه يبدو مشابهًا بشكل ملحوظ للاقتراح الوارد في https://github.com/rust-lang/rust/issues/20021.

ربما يجب إضافة https://github.com/rust-lang/rust/issues/36479 إلى رأس المشكلة أيضًا.

varkor هو مشابه جدا. مع عملك في الكود الذي لا يمكن الوصول إليه ، هل لاحظت أي مشاكل في رمز مثل هذا يتم الآن تمييزه على أنه لا يمكن الوصول إليه ؟:

impl fmt::Debug for ! {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        *self    // unreachable!
    }
}

لأن هذا يستلزم إلى حد كبير قبول نوع من الاقتراح مثل هؤلاء.

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

جزء من هذا سيكون مؤشرات لاختبار الحالات التي توضح السلوك المعني.

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

السؤال ذو الصلة: هل يجب أن نحاول حل https://github.com/rust-lang/rust/issues/46325 قبل أن نستقر؟ ربما لا يهم.

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

canndrew : لا أعتقد أنني نظرت حقًا في أي حالات من هذا القبيل ، على الرغم من أنني أعتقد بالتأكيد أن القدرة على حذف التطبيقات المستحيلة ستكون مطلوبة بشدة عند استقرار ! .

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

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

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

nikomatsakis شيء من هذا القبيل؟

ملخص المشكلة - استقرار !

ما يتم استقراره

  • ! هو الآن نوع كامل ويمكن استخدامه الآن في أي موضع من النوع (مثل RFC 1216 ). يمكن للنوع ! الإجبار على أي نوع آخر ، راجع https://github.com/rust-lang/rust/tree/master/src/test/run-fail/adjust_never.rs للحصول على مثال.
  • ستعمل الآن كتابة الاستدلال على متغيرات النوع غير المقيد الافتراضية إلى ! بدلاً من () . تم إيقاف العمل بـ resolve_trait_on_defaulted_unit lint. مثال على مكان ظهور ذلك إذا كان لديك شيء مثل:

    // We didn't specify the type of `x`. Under some circumstances, type inference
    // will pick a type for us rather than erroring
    let x = Deserialize::deserialize(data);
    

    بموجب القواعد القديمة ، سيؤدي هذا إلى إلغاء تسلسل () ، بينما بموجب القواعد الجديدة ، سيتم إلغاء تسلسل ! .

  • البوابة المميزة never_type مستقرة ، على الرغم من أن بعض السلوكيات التي استخدمتها للبوابة تعيش الآن خلف بوابة الميزات الجديدة exhaustive_patterns (انظر أدناه).

ما لا يتم استقراره

  • مطابقة الأنماط الشاملة للأنواع غير المأهولة. على سبيل المثال

    let x: Result<u32, !> = ...;
    let Ok(y) = x;
    

    سيظل هذا الرمز يشكو من أن Ok(_) نمط قابل للدحض. يمكن إصلاح ذلك باستخدام البوابة المميزة exhaustive_patterns . راجع RFC 1872 للتقدم في هذه المسألة. راجع https://github.com/rust-lang/rust/tree/master/src/test/ui/feature-gate-exhaustive-patterns.rs لمعرفة حالة الاختبار التي تؤكد أن هذا السلوك لا يزال محصورًا.

هل يجب أن نحاول حل # 46325 قبل أن نستقر؟

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

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

شيء من هذا القبيل؟

نعم شكرا! هذا جيد.

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

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

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

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

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

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

منتهي.

تضمينrfcbot fcp

أقترح تثبيت النوع ! - أو جزء منه على الأقل ، كما هو موضح هنا . العلاقات العامة هنا .

جزء واحد من البيانات التي أريدها هو تشغيل فوهة البركان. أنا بصدد إعادة تعيين https://github.com/rust-lang/rust/pull/47630 (نظرًا لأن canndrew لا يستجيب

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

  • [x] Kimundi
  • [x]alexcrichton
  • []aturon
  • [x]cramertj
  • [x] dtolnay
  • [x]eddyb
  • [x]nikomatsakis
  • [x]nrc
  • []pnkfelix
  • [x]sfackler
  • [x] @ بدون قوارب

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

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

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

أوه ، بعض الأشياء التي تذكرتها للتو:

  • يجب أن ننظر في فكرة تثبيت هذا فقط في العصر الجديد . على وجه الخصوص ، التغيير إلى القواعد الاحتياطية غير متوافق مع الإصدارات السابقة - سيعطينا تشغيل فوهة البركان نوعًا من الحد الأدنى على التداعيات ، لكنه مجرد حد أدنى. ولكن ربما يمكننا الاحتفاظ بالقواعد الاحتياطية القديمة إلا إذا كنت في العصر الجديد.
  • ثانيًا ، أعتقد أن جزءًا من الخطة هنا كان أيضًا وجود بعض الإرشادات عندما يكون من المناسب تنفيذ سمة مقابل ! . TL ؛ DR هو أنه لا بأس إذا كانت الأساليب الموجودة في السمة غير قابلة للاستخدام دون توفير قيمة ! - لذا فإن تنفيذ Clone مقابل ! لا بأس به ، على ما أعتقد ، ولكن تنفيذ Default ليس كذلك. بعبارة أخرى ، إذا كان تطبيق الضمانات يتطلب منك panic! تمييز ، فهذه علامة سيئة. =)

nikomatsakis هل يمكننا تغيير القاعدة الاحتياطية في حقبة جديدة ولكن ما زلنا نجعل ! كنوع متاح في حقبة 2015؟

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

يجب أن ننظر في فكرة تثبيت هذا فقط في العصر الجديد .

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

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

هذا مذكور في المستندات مقابل !

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

هل يمكننا تغيير القاعدة الاحتياطية في حقبة جديدة ولكن ما زلنا نصنعها! كنوع متوفر في عصر 2015؟

نعم

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

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

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

TL ؛ DR هو أنه لا بأس إذا كانت الطرق الموجودة في السمة غير قابلة للاستخدام دون توفير قيمة !

هذه مجرد قاعدة عادية - يمكنك إضافة ضمني عندما يمكنك تنفيذه بطريقة عاقلة ، حيث يستبعد "تنفيذه بطريقة عاقلة" الذعر ، ولكنه يتضمن "ex falso" UB في وجود بيانات غير صالحة.

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

ربما سيكون من المفيد أن يكون لديك طريقة آمنة fn absurd(x: !) -> ! موثقة لتكون طريقة آمنة للتعبير عن رمز لا يمكن الوصول إليه أو في مكان ما في libstd؟ أعتقد أنه كان هناك RFC لذلك ... أو على الأقل مشكلة RFC: https://github.com/rust-lang/rfcs/issues/1385

RalfJung أليست هذه absurd فقط identity ؟ لماذا هو مفيد؟

إنها ليست مثل الجوهر unsafe fn unreachable() -> ! الذي لا يأخذ أي حجة وهو سلوك غير محدد للغاية.

حسنًا ، نعم ، في الغالب. كنت أستخدم نوع الإرجاع ! بمعنى "لا ينتهي". (النوع المعتاد من absurd هو fn absurd<T>(x: !) -> T ، لكن هذا لا يبدو مفيدًا في Rust أيضًا.)

كنت أفكر في أن هذا قد يساعد في "يميل الناس إلى الخلط بشأن أشياء مثل هذه في وجود ! " - وجود بعض الوثائق في مكان ما أن التفكير "ex falso" شيء.

يبدو أنني حصلت أيضًا على المشكلة الخاطئة ... اعتقدت في مكان ما أنه كان هناك نقاش حول وظيفة "ex falso". يبدو أنني مخطئ.

يمكن أيضًا كتابة fn absurd<T>(x: !) -> T match x {} ، لكن من الصعب إيجاد ذلك إذا لم تره من قبل. إنه على الأقل يستحق الإشارة إليه في rustdoc وفي الكتاب.

الجبهة الوطنية سخيفة(x:!) -> يمكن أيضًا كتابة T مباراة x {} ، ولكن يصعب التوصل إليها إذا لم تكن قد شاهدتها من قبل.

حسنًا ، لهذا السبب اقترحت طريقة في مكان ما في libstd. لست متأكدا أين أفضل مكان بالرغم من ذلك.

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

يمكن أيضًا كتابة fn absurd<T>(x: !) -> T match x {}

يمكن كتابتها أيضًا كـ x (AFAIK) - تحتاج فقط match إذا كان لديك enum فارغ.

قد تكون الدالة absurd(x: !) -> T مفيدة في التمرير إلى وظائف ذات ترتيب أعلى. لقد احتجت إلى هذا (على سبيل المثال) عند تسلسل العقود الآجلة ، أحدها به نوع الخطأ ! ونوع الخطأ الآخر T . على الرغم من أن التعبير من النوع ! يمكن الإكراه على T هذا لا يعني أنه سيتم توحيد ! و T ، لذلك في بعض الأحيان ما زلت بحاجة إلى ذلك يدويًا تحويل النوع.

وبالمثل ، أعتقد أن الأنواع التي تشبه Result يجب أن تحتوي على طريقة .infallible() والتي تحول Result<T, !> إلى Result<T, E> .

يجب أن تحتوي الأنواع الشبيهة بالنتائج على طريقة .infallible () التي تحول النتيجةللنتيجة.

كنت أتوقع أن يكون لمثل هذه الطريقة نوع Result<T, !> -> T .

كنت أتوقع أن يكون لمثل هذه الطريقة نوع النتيجة-> T.

أليس هذا هو نفسه unwrap ؟ سيتم تحسين حالة الذعر بعيدًا نظرًا لأن حالة Err لا يمكن الوصول إليها.

Amanieu النقطة unwrap يضيف مسدس قدم أثناء إعادة البناء ، فلن يصبح الذعر رمزًا حيًا مرة أخرى.

قراءة unwrap مثل assert - "فحص وقت التشغيل للأمام" (حتى أن IIRC كانت هناك مقترحات لإعادة تسميتها لكنها جاءت بعد فوات الأوان ... للأسف). إذا كنت لا تريد فحص وقت التشغيل وتحتاج إليه ، فإن infallible سيجعل ذلك صريحًا.

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

rfcbot fcp إلغاء

aturon و pnkfelix لم

تم إلغاء اقتراح

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

  • [x]alexcrichton
  • [x]aturon
  • [x]cramertj
  • [x] dtolnay
  • [x]eddyb
  • [x]nikomatsakis
  • [x]nrc
  • []pnkfelix
  • [x]sfackler
  • [x] @ بدون قوارب

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

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

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

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

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

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

وهذا يترك فقط الانحدار الفعليين: oplog-0.2.0 (في الواقع هو تبعيته bson-0.3.2 الذي تم كسره) و rspec-1.0.0-beta.4. كلاهما يعتمدان على السلوك الاحتياطي القديم ، على الرغم من أنهما لحسن الحظ أنهما يتعطلان في وقت التجميع ، وليس وقت التشغيل. سأرسل العلاقات العامة لإصلاح هذه الصناديق.

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

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

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

أعلم أنني متحيز ، لكني أرى تغيير السلوك الاحتياطي باعتباره أكثر من إصلاح الخطأ مقارنة بأشياء مثل dyn Trait و catch .

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

حسنًا ، أقول دعونا فقط نغيرها.

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

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

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

هل هذه المربعات المعلقة في أعلى منشور لم يتم تحديدها بعد؟ هل هناك أي شيء تم التراجع عنه في تلك القائمة؟

earthengine لا أعتقد أن الاثنين اللذان

في الأساس أن تكون مجموعة صغيرة جدًا.

nikomatsakis لقد أنشأت للتو مشكلة الملخص هنا: https://github.com/rust-lang/rust/issues/48950

هل كان هناك أي اعتبار لجعل ! نمطًا يطابق قيم النوع ! ؟ (لقد أجريت grep سريعًا من خلال هذه المشكلة ومسألة RFC الأصلية ، لكن لم أجد أي شيء ذي صلة).

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

كمثال ، معطى

let foo: Result<String, (!, String)> = Ok("hello".to_owned());

هذا من شأنه أن يسمح لكتابة أكثر وضوحا

let bar: Result<String, String> = foo.map_err(|(!, _)| unreachable!());

بدلا من المعرضة للخطأ المحتمل

let bar: Result<String, String> = foo.map_err(|_| unreachable!());

أو الأقل عرضة للخطأ

let bar: Result<String, String> = foo.map_err(|(e, _)| e);

تحرير: إعادة قراءة https://github.com/rust-lang/rfcs/pull/1872 التعليقات تذكر تقديم نمط ! . هذا محض في سياق تعبيرات match بالرغم من ذلك ، لست متأكدًا مما إذا كان يتم تعميمه على وسيطات الإغلاق / الوظيفة.

@ Nemo157

هل كان هناك أي اعتبار لجعل! نمط يطابق قيم! نوع؟

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

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

fn foo() -> Result<String, String> {
    let bar: Result<String, (!, String)> = Ok("hello".to_owned());
    Ok(bar?)
}

سيستمر هذا في التوسع كما هو الحال اليوم (_لست متأكدًا بنسبة 100٪ أن هذا هو التوسيع الصحيح ، لكن يبدو أنه قريب بدرجة كافية _)

fn foo() -> Result<String, String> {
    let bar: Result<String, (!, String)> = Ok("hello".to_owned());
    Ok(match Try::into_result(bar) {
        Result::Ok(e) => e,
        Result::Err(e) => return Try::from_err(From::from(e)),
    })
}

ولكن لن يتم فحص نوع الفرع Result::Err ولن تحصل على الخطأ the trait bound `std::string::String: std::convert::From<(!, std::string::String)>` is not satisfied الذي تحصل عليه اليوم.

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

! هو نوع إيجابي ، مثل bool ، لذلك من المستحيل نوعًا ما مطابقة النمط عليه في قائمة وسيطة الإغلاق دون توسيع بناء جملة الإغلاق للسماح بفروع متعددة. على سبيل المثال ربما يمكننا السماح للإغلاق bool -> u32 بكتابة شيء مثل (بناء جملة افتراضي قبيح) [~ |false| 23, |true| 45 ~] -> u32 ، ثم يمكن كتابة الإغلاق من أي نوع فارغ إلى u32 [~ ~] -> u32 .

اكتب الأمثلة الأصلية الخاصة بك ، يمكنك كتابة foo.map_err(|_: (!, _)| unreachable!()) الرغم من أنني أفضل foo.map_err(|(e, _)| e) لأنه يتجنب استخدام unreachable! .

! نوع إيجابي

ماذا تقصد بالنوع الإيجابي؟

من المستحيل نوعًا ما مطابقة النمط عليه في قائمة وسيطات الإغلاق دون توسيع بناء جملة الإغلاق للسماح بفروع متعددة

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

let foo: Result<String, ((), String)> = Ok("hello".to_owned());
let bar: Result<String, String> = foo.map_err(|((), s)| s);

أود فقط أن أعتبر النمط ! كنمط لا يمكن دحضه يطابق جميع القيم 0 من النوع ! ، وهو نفس النمط () لا يمكن دحضه والذي يطابق جميع قيم 1 لـ النوع () .

سأفكر فقط في! النمط كنمط لا يمكن دحضه يطابق جميع القيم 0 لـ! type ، نفس () هو نمط لا يمكن دحضه يطابق جميع القيم 1 من النوع ().

حسنًا ، لكن 0! = 1. ؛) من غير المجدي كتابة أي رمز بعد المطابقة على ! - على سبيل المثال إذا كان نوع الوسيطة هو (!, i32) ، ثم |(!, x)| code_goes_here سخيف لأن هذا الرمز ميت على أي حال. ربما أكتب |(x, _)| match x {} لأوضح بوضوح أن x هو عنصر ! . أو باستخدام الوظيفة absurd اقترحتها أعلاه ، |(x, _)| absurd(x) . أو ربما |(x, _)| x.absurd() ؟

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

سيء جدًا ، لا يمكننا إضافة impl<T> From<!> for T الآن. (يتداخل مع From<T> for T ، ما لم يكن هناك شيء تخصص؟) ربما لا يمكننا إضافته لاحقًا. (في دورات الإصدار بعد استقرار ! ، قد تنفذ بعض الصناديق From<!> for SomeConcreteType .)

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

كنت أفكر بجدية في الاختراق لمرة واحدة

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

SimonSapin ما مدى سرعة / سهولة إضافة هذا الاختراق؟ سيكون من الممتع حقًا ألا تكون قادرًا على الحصول على From<!> for T .

بدلاً من ذلك ، يمكننا إرسال تحذير سريعًا ضد تنفيذ From<!> for SomeConcreteType .

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

للأسف التخصص:

  • سيتطلب تغيير From::from إلى default fn ، والذي سيكون "مرئيًا" خارج std. لا أعرف ما إذا كنا نريد أن نفعل ذلك في السمات التي تنتقل بالاتصال الجنسي طالما أن التخصص غير مستقر.
  • كما هو مقبول في RFC 1210 ، لا يدعم تحديدًا ميزة اللغة التي كنت أفكر فيها (إزالة غموض ضمنيين متداخلين حيث لا يكون أي منهما أكثر تحديدًا من الآخر عن طريق كتابة ضمني ثالث للتقاطع).

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

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

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

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

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

وأتساءل ما impls الآخر إلى جانب T: From<!> لا يمكن إضافة بعد ! تستقر. ربما يجب أن نحاول الحصول على مؤشر على كل هذه الضمانات المعقولة قبل الاستقرار ، على عكس الضمانات لـ ! بشكل عام ، حيث لا يوجد مثل هذا الاندفاع.

النشر المشترك من هذا التعليق :

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

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

(الترشيح لمناقشة lang-team حول هذه النقطة.)

خلال الاجتماع ، تراجعنا قليلاً عن بعض الخيارات هنا - أشعر بالتردد في إجراء تغييرات ، غالبًا لأنه يغير دلالات الكود الحالي في بعض الحالات الجانبية ، وليس من الواضح ما إذا كانت القواعد الجديدة أفضل . لكن في الغالب أعتقد أننا قررنا أنه سيكون من الرائع محاولة جمع إيجابيات / سلبيات كل تصميم مرة أخرى حتى نتمكن من إجراء مناقشة أكثر اكتمالاً. أرغب في رؤية قائمة بحالات الاستخدام بالإضافة إلى المزالق. ذكر cramertj رغبتهم في Ok(33) وجود نوع يعود إلى Result<i32, !> ، بدلاً من الخطأ كما يحدث اليوم ؛ أعتقد أنهم كانوا يقولون بعد ذلك أن الاحتفاظ بالاحتياطي من return مثل () سيكون غير متسق مع مثل هذا التغيير (على الرغم من أنها متعامدة) ، وقد يؤدي إلى حدوث تعارضات في المستقبل. هذا عدل.

التحدي الذي يواجهه مع تعليقات Err (# 40801) هو أن هناك حالات متطرفة هناك أيضًا ، مثل:

let mut x = Ok(22);
x = Err(Default::default());

حيث يُستدل في النهاية على نوع x إلى Result<T, !> .

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

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

لمعلوماتك ، تسببت هذه الميزة في حدوث تراجع في 1.26: # 49932

(تعيين الملصقات لكل https://github.com/rust-lang/rust/issues/35121#issuecomment-368669041.)

هل يجب أن يشير عنصر قائمة التحقق حول الإكراه إلى ! إلى https://github.com/rust-lang/rust/issues/50350 بدلاً من الإشارة إلى هذه المشكلة؟

إذا كنت أرغب في استقرار هذا في أسرع وقت ممكن ، فأين هو أفضل مكان لتوجيه طاقتي؟

remexre ، أعتقد أن أفضل وصف للمشكلة التي تؤدي إلى عودة الاستقرار هو https://github.com/rust-lang/rust/issues/49593

لذلك سيكون الحل العملي هو مربع الحالة الخاصة باعتباره نوعًا فرعيًا من
علبة؟ هل هناك أي سمات آمنة للأشياء لا يمكن التعامل معها بهذه الطريقة؟

في الأحد ، 8 يوليو (تموز) 2018 ، الساعة 8:12 صباحًا ، كتب Ralf Jung [email protected] :

remexre https://github.com/remexre أعتقد أن أفضل وصف لملف
المشكلة التي تؤدي إلى عودة الاستقرار هي # 49593
https://github.com/rust-lang/rust/issues/49593

-
أنت تتلقى هذا لأنه تم ذكرك.

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

>

شكرا،
ناثان

يجب أن ينتقل هذا النقاش حقًا إلى https://github.com/rust-lang/rust/issues/49593 ، ولكن جزءًا أساسيًا منه هو أن Box::<T>::new يتطلب T: Sized ، لكن هذا new يُستنتج أن T = Error (سمة DST) بناءً على نوع الإرجاع.

بخلاف عدم مراعاة الحالة الخاصة ، هل هناك أي مشكلات تتعلق بالغلاف الخاص Box::new ليكون:

Box::new : T -> Box<U>
where T <: U,
      T: Sized

أو

Box::new : T -> Box<U>
where Box<T> <: Box<U>,
      T: Sized

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

إذا كان ! هو Sized إذن ، فلا شيء يمنعنا من تجميع Box::new(x as !) ، لكن هذه طريقة أخرى إلى panic! ، حيث لا يمكن لأي نموذج ذاكرة فعلاً تخصيص usize::MAX بايت.

لا يبدو واضحًا بالنسبة لي أن ! يجب أن يكون له حجم لانهائي / usize::MAX مقابل كونه ZST؟

use std::mem::size_of;
enum Void {}
fn main() { println!("{}", size_of::<Void>()); }

حاليا هو 0.

تم شرح السبب في النص المقدم.

bool : قيمتان صالحتان => السجل (2) / السجل (2) = 1 بت
() : 1 قيم صالحة => تسجيل (1) / سجل (2) = 0 بت
! : 0 قيم صالحة => log (0) / log (2) = بت Inf

كشخص ليس على دراية بالنظرية ذات الصلة ، أرى التمسك بالصيغة log(x)/log(y) بقدر ما يلاحق أناقة النموذج النظري على حساب الاستخدام العملي. (AKA ذكي للغاية من أجل مصلحته)

حدسيًا ، يبدو أن ! يجب أن يكون حجمه صفريًا أيضًا للأسباب التالية:

  • bool : يحتاج إلى مساحة للتمييز بين قيمتين صالحتين => log (2) / log (2) = 1 bit بالإضافة إلى نظام الكتابة
  • () : يحتاج إلى مساحة لتمييز قيمة واحدة صالحة => لا توجد مساحة مطلوبة خارج نظام الكتابة
  • ! : يحتاج إلى مساحة لتمييز أي قيم صالحة => لا توجد مساحة مطلوبة خارج نظام الكتابة

حسنًا ، إنه في الواقع - اللانهاية ، وهو أمر منطقي ، حيث أن وضع! كحقل إلى هيكل يحول الهيكل بشكل أساسي إلى! كذلك (c + -inf = -inf). لذلك بما أننا نتعامل مع الاستخدام ، فإن 0 هي أقرب قيمة لدينا لتمثيل ذلك. (أعتقد أن التنفيذ الفعلي في rustc حتى يستخدم -inf).

حدسيًا ، يبدو أن ! يجب أن يكون أيضًا بحجم صفري

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

0 قيم صالحة => السجل (0) / السجل (2) = بتات Inf

سجل (0) غير محدد ؛ انها ليست لانهائية.

من ناحية أخرى ، فإن الحصول على حجم usize::MAX هو طريقة بسيطة لفرض عدم السكن. يوافق على؟

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

يتناسب هذا أيضًا مع المفهوم الذي كنت أحاول استنتاج صحته ، حيث يريد حدسي رؤية ! على أنه "ZST على مستوى آخر تمامًا". (على سبيل المثال ، ! هو نوع النظام مثل () لتخصيص الذاكرة.)

تضمين التغريدة
الصيغة الخاصة بك -Inf + c == -Inf منطقية ، ولكن عند استبدال -Inf بـ 0usize ، لم يعد الأمر كذلك.

من ناحية أخرى ، إذا كان الحرف الحسابي "caped": أي فائض يحسب إلى usize::MAX فإن usize::MAX يناسب الصيغة بشكل مثالي.

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

لماذا لا يقتصر الأمر على "تثبيت" حجم أي نوع يحتوي على نوع فارغ ( ! ، أو enum Void {} ) من تحديد المستخدم إلى size=0,alignment=0 ؟ يبدو هذا أكثر منطقية من الناحية المعنوية بالنسبة لي ...

تضمين التغريدة
لأن هذا لا يعمل. مثال: sizeof(Result<usize,!>) == sizeof(usize) .

حسنًا ، لا يحتوي Result<usize, !> على ! مباشرةً ؛ sizeof(Result::Ok(usize)) == 8 ، sizeof(Result::Err(!)) == 0

لا ، sizeof(Result::Err(!)) == usize::MAX في اقتراحي ، لأن Result::Err(!) :: Result<!,!> .

يجب أن تكون القاعدة: في enum s ، يُعتبر حجم ! أقل من جميع قيم الحجم الأخرى ، ولكن في structs ، هذا هو الحد الأقصى للحجم الذي يمكن تصويره على الإطلاق.

استنتاج:

  • ! ليس بالتوقيت الصيفي. التوقيت الصيفي ليس Sized ليس لأنه ليس لها حجم ، ولكن لأن معلومات الحجم مخفية. ولكن مقابل ! نعرف كل شيء عنها.
  • يجب أن يكون حجم ! وبالتالي يجب السماح لـ Box::new(x as !) على الأقل بالتجميع.
  • يجب أن يكون حجم struct s يحتوي على ! كما لو كان ! ، enum s يحتوي على ! مباشر في المتغير يجب أن يكون بحجم إذا كان هذا المتغير غير موجود.

إما أن نأخذ في الاعتبار sizeof(!)==0 أو sizeof(!)==usize::MAX ، علينا أن نقدم بعض العناصر الحسابية الخاصة للسماح بما ورد أعلاه:

sizeof(!)==0 : في الهياكل ، 0 + n = 0 : قلق: ، بالأرقام ، max(0,n) = n : أحمر الخدود :.
sizeof(!)==usize::MAX : في الهياكل ، usize::MAX + n =usize::MAX : محايد_وجه: ، تعدادات ، max(usize::MAX,n) = n : مسح :.

هناك فائدة لصالح usize::MAX الرغم من ذلك: فهو يمنع تقنيًا أي محاولات ، حتى unsafe منها ، لبناء مثل هذا الكائن ، لأنه غير ممكن في أي نظام حقيقي.

هناك فائدة لصالح usize :: MAX على الرغم من ذلك: فهو يمنع تقنيًا أي محاولات ، حتى غير آمنة ، لبناء مثل هذا الكائن ، لأنه غير ممكن في أي نظام حقيقي.

أعني ، إذا كان مسموحًا باستخدام خدع unsafe : *transmute::<Box<usize>, Box<!>>(Box::new(0)) يحصل علي ! بغض النظر عن حجمه. usize::MAX يزيد من احتمالية خطأ المترجم إذا حاول شخص ما تنفيذ std::mem::zeroed<!>() ، كما أفترض ، لكنني سأضع العبء على الشخص الذي يكتب هذا الرمز غير الآمن ، بدلاً من الرياضيات غرابة أعلاه.

لاحظ أن حجم ! _actually_ 0 - وليس MAX ولا -INF -المشبع إلى 0 - مطلوب للتعامل مع التهيئة الجزئية ، كما كان تم العثور عليها في https://github.com/rust-lang/rust/issues/49298#issuecomment -380844923 (وتم تنفيذها في https://github.com/rust-lang/rust/pull/50622).

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

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

Box::new : T -> Box<U>
where T <: U,
      T: Sized

Box هو (في الغالب) نوع مكتبة ويتم تعريف طريقة new في تركيب Rust في src/liballoc/boxed.rs . يفترض الوصف الخاص بك عامل تشغيل <: غير موجود في Rust. هذا لأن التصنيف الفرعي في Rust موجود فقط من خلال معلمات العمر. إذا كنت ترغب في قراءة أو مشاهدة المزيد:

! يمكن الإكراه على أي نوع، ولكن هذا هو أضعف من أن تكون أي نوع. (على سبيل المثال ، Vec<&'static Foo> هو أيضًا Vec<&'a Foo> لأي 'a ، لكن لا يوجد تحويل ضمني من Vec<!> إلى Vec<Foo> : http: / /play.rust-lang.org/؟gist=82d1c1e1fc707d804a57c483a3e0198f&version=nightly&mode=debug&edition=2015)

أعتقد أن الفكرة هي أنه يمكنك فعل ما تعتقد أنه مناسب في وضع unsafe ، لكن عليك مواجهة UB إذا لم تفعل ذلك بحكمة. إذا كان الحجم الرسمي لـ ! هو usize::MAX ، يجب أن ينبه هذا المستخدم بما فيه الكفاية بحيث لا يجب أن يتم حث هذا النوع على الإطلاق.

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

تضمين التغريدة
أركز على المشكلة للتأكد من عدم وجود مشكلة في Box::new(x as !) حتى نتمكن من المضي قدمًا في تثبيت هذا الأمر. الطريقة المقترحة هي جعل حجم ! . إذا كان ZST هو المعيار ، فيجب أن يكون جيدًا. أنا لا أجادل بعد الآن. فقط قم بتنفيذ مثل هذا sizeof(!)==0 سيفي بالغرض.

من الطبيعي أن تضبط محاذاة تستخدم أيضًا :: MAX

usize::MAX قوة العدد اثنين ، لذا لا تعد محاذاة صحيحة. أيضًا ، لا يدعم LLVM المحاذاة التي تزيد عن 1 << 29 . و ! لا يجب أن يكون له محاذاة كبيرة على أي حال ، لأن let x: (!, i32); x.1 = 4; يجب أن يأخذ 4 بايت فقط من المكدس ، وليس غيغابايت أو أكثر.

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

المشكلة التي تحدث هنا هي أن Box::new(!) هو Box<$0> حيث نتوقع Box<Error> . مع وجود عدد كافٍ من التعليقات التوضيحية ، سنطلب Box<$0> ليتم إجبارنا على Box<Error> ، والذي سيترك لنا لاحقًا $0 افتراضيًا إلى ! .

ومع ذلك ، فإن $0 هو متغير من النوع ، لذلك لا يتم الإكراه ومن ثم نحصل على $0 = Error .

يتمثل أحد خيارات الاختراق لإصلاح الأشياء في العثور على قيد $0: Sized (اتباع الأنواع الفرعية؟) وإدخال الإكراه المرتبط بذلك ، بحيث لا يزال يتم استدعاء Box في النوع.

لقد فعلنا بالفعل شيئًا كهذا لاكتشاف Fn في الإغلاق ، لذلك لن يكون هذا امتدادًا. لا يزال قبيح.

هذا كل شيء ، إذا واجهنا أثناء الإكراه التزامًا من النموذج $0: Unsize<Y> حيث Y بالتأكيد غير بحجم و $0 هو بالتأكيد الحجم ، لا تعامل ذلك على أنه غموض بل بالأحرى تعامل معه على أنه "موافق" واستمر في الإكراه غير المقيد.

@ arielb1

فكيف يختلف هذا عن الإكراه Box::new(()) إلى Box<Debug> ؟ std::error::Error هي سمة مثل Debug ، وليست من النوع مثل [u8] . من ناحية أخرى ، فإن std::io::Error هو نوع ، لكنه Sized .

نحن لا نواجه مشكلة في ما يلي:

use std::fmt::Debug;

fn f(x:()) -> Box<Debug> {
    Box::new(x)
}

fn main() {
}

earthengine ، الفرق بين هذه المشكلة

| مكتوب | Box::new(!): Box<Debug> | Box::new(()): Box<Debug> |
| ------ | ------ | ------- |
| أجرى | Box::new(! as Debug): Debug | (Box::new(()) as Box<Debug>): Debug |

بشكل أساسي ، نظرًا لأن ! يمكنه الإكراه على أي شيء ، فإنه يتم إجباره على Debug قبل الاتصال بـ Box::new . هذا ليس خيارًا مع () ، لذلك لا توجد مشكلة في الحالة الثانية - يحدث الإكراه هناك بعد Box::new .

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

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

على سبيل المثال ، لا ينبغي ترجمة ما يلي:

let v: str = !;
let v: [u8] = !;
let v: dyn Debug = !;

ببساطة لأنه لا يمكنك استبدال ! بأي تعبير Rust موجود لجعله مترجمًا.

يحرر

هذا ليس خيارًا مع ()

لذلك يمكن لأي شخص أن يشرح لماذا؟ إذا لم يتم تجميع () as dyn Debug فلن يتم ترجمة ! as dyn Debug والعكس صحيح. &() as &Debug ، وكذلك &! as &Debug ، لا توجد مشاكل. إذا تم تجميع () as dyn Debug يومًا ما ، فستتكرر المشكلة التي نواجهها اليوم مقابل () ، لذلك سيتعين على منفذي DST RFC الاستحقاق معها ، وبالتالي ستحل نفس المشكلة التي لدينا مقابل ! .

! يجبر على أي شيء لأنه من المستحيل أن يوجد. "ex falso quodlibet". لذلك يجب تجميع جميع الأمثلة الخاصة بك - لا يوجد سبب نظري جيد لعمل استثناء خاص للأنواع غير الحجم.

هذا لا ينتهك حتى "DTS لا يمكن أن توجد" لأن ! لا يمكن أن يكون موجودًا أيضًا. :)

() as dyn Debug لا يجمع

لا أعرف ما هي الخطط الخاصة بـ rvalues ​​غير المحسّنة ، لكنني أعتقد أنها يمكن أن تجعل هذا التجميع؟
النقطة المهمة هي أن القيام بذلك مقابل ! لا يتطلب منا تنفيذ قيم rvalues ​​غير الحجم لأن هذا يمكن أن يحدث فقط في الكود الميت.

ومع ذلك ، ربما تشير إلى حل محتمل هنا (وربما هذا ما اقترحه @ arielb1 أيضًا أعلاه): هل من الممكن تقييد الإكراه ! لتطبيقه فقط إذا كان نوع الهدف (معروفًا بالحجم) ؟ لا يوجد سبب نظري جيد للقيام بذلك ولكن هناك سبب عملي. : D وهي أنه قد يساعد في حل هذه المشكلة.

عندما قلت "DTS لا يمكن أن توجد" أعني في نحوي. ليس من الممكن أن يكون لديك متغير محلي كتبته str ، على سبيل المثال.

من ناحية أخرى ، لا يمكن أن يكون ! موجودًا فهو دال. يمكنك كتابة

let v = exit(0);

أن يكون لديك v يكتب بنائيًا ! في السياق ، ولكن بسبب عدم تشغيل الربط وبالتالي لا يخرج في العالم الحقيقي.

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

ينطبق هذا أيضًا على قيم rvalues ​​غير الحجم ، لذلك قبل أن يكون لدينا قيم rvalues ​​غير الحجم ، لا يُسمح بإجبار ! على الأنواع غير الحجم ، حتى نعرف كيفية التعامل مع قيم rvalues ​​غير الحجم. وعند هذه النقطة فقط يمكننا البدء في التفكير في هذه المشكلة (يبدو أن أحد الحلول الواضحة هو السماح Box::new باستلام قيم غير بحجم).

ليس من الممكن أن يكون لديك متغير محلي يكتب هو str ، على سبيل المثال.

هذا مجرد قيود على التنفيذ الحالي: https://github.com/rust-lang/rust/issues/48055

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

هذه ليست المشكلة هنا.

مرة أخرى ، لنفترض أننا في موقف يكون فيه Box::new(!: $0): Box<dyn fmt::Debug> حيث $0 متغير نوع.

ثم يمكن للمجمع بسهولة أن يستنتج بسهولة أن $0: Sized (لأن $0 يساوي معلمة النوع Box::new ، التي لها T: Sized ملزمة).

تأتي المشكلة حيث يحتاج المترجم لمعرفة أي نوع من الإكراه لاستخدامه لتحويل Box<$0> إلى Box<dyn fmt::Debug> . من وجهة نظر "محلية" ، هناك حلان:

  1. لديك قسر CoerceUnsized ، يتطلب Box<$0>: CoerceUnsized<Box<dyn fmt::Debug>> . هذا برنامج صالح مقابل $0 = ! وسيؤدي التقصير إلى جعله مترجمًا لذلك.
  2. لديك إكراه على الهوية ، مع $0 = dyn fmt::Debug . هذا لا يتوافق مع متطلبات $0: Sized .

لا يرغب المترجم في فتح الكثير من العناصر عند التفكير في الغموض ، حيث يمكن أن يتسبب ذلك في حدوث مشكلات في الأداء ومشكلات يصعب تصحيحها ، لذلك فهو يختار الإكراه الذي يجب استخدامه بطريقة غبية إلى حد ما (على وجه الخصوص ، لا نفعل ذلك) 't have T: CoerceUnsized<T> ، لذلك إذا اختار المترجم الخيار 1 فإنه يمكن أن "يتعطل" بسهولة كبيرة)، وينتهي الأمر باختيار الخيار 2، والذي فشل. كانت لدي فكرة لجعلها أكثر ذكاءً واختيار الخيار 1.

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

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

قم بإجبار الهوية ، باستخدام $ 0 = dyn fmt :: Debug. هذا غير متوافق مع $ 0: متطلب الحجم.

بالكاد أستطيع أن أرى لماذا يجب أن نسمح ب let v: str=! لكن ليس let v: str; . إذا كنت لا تستطيع حتى التصريح عن متغير بنوع معين ، فلماذا يمكنك ربط قيمة (ربما مستحيلة) به؟

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

لذا فإن استنتاجي هو أن مشكلة Box::new(!) as Box<Error> هي مشكلة حظر لقيم rvalues ​​غير الحجم ، وليس للنوع ! . يجب أن تكون قواعد الإكراه ! يلي:

يمكنك كتابة let v: T ، إذا وفقط إذا كان يمكنك كتابة let v: T = ! .

ثم يتم تقييم المعنى الفعلي باستخدام اللغة. على وجه الخصوص ، بعد أن يكون لدينا قيم غير محددة الحجم ، لدينا Box::new(! as dyn Debug) ولكن قبل ذلك ، أود أن أقول لا.

إذن ما الذي يمكننا فعله للمضي قدمًا؟

أضف بوابة ميزة على الكود الذي يؤدي إلى الإكراه غير الحجم ، إذا كانت قيمة rvalue غير الحجم قيد التشغيل ، فجرب هذا الإكراه ، وإلا فتخطاه. إذا كان هذا هو الإكراه الوحيد المتاح ، فيجب أن تكون رسالة الخطأ "السمة المقيدة str: std::marker::Sized غير راضية" ، كما هو الحال في الحالات المماثلة. وبالتالي

علينا أن نرى أنه يمكن حساب ذلك دون الإضرار بالأداء في الوظائف الأكبر.

تم تناوله: مجرد فحص بسيط للميزة.

في غضون ذلك ، أضف مشكلة جديدة لقيم rvalues ​​غير الحجم ،

إنستيريستينج.

هذه

fn main() {
    let _:str = *"";
}

يجمع ، ولكن

fn main() {
    let v:str = *"";
}

لاتفعل. هذا لا يرتبط حتى بالنوع ! . هل يمكنني إنشاء مشكلة لهذا؟

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

في المثال الأول ، يعتبر النمط _ مميزًا من حيث أنه لا يطابق أي شيء فقط (مثل ربط المتغير المحلي) ، ولكنه لا يُنشئ متغيرًا على الإطلاق. لذا فإن هذا الرمز هو نفسه fn main() { *""; } بدون let . لا يعد إلغاء الإشارة إلى مرجع (حتى التوقيت الصيفي) ثم عدم القيام بأي شيء بالنتيجة مفيدًا ، ولكن يبدو أنه صالح ولست مقتنعًا بأنه يجب أن يكون غير صالح.

حق. لكنه محير حقًا ، خاصة ما يلي

fn main() {
    let _v:str = *"";
}

لا يجمع سواء. مع نظريتك حول _ يجب أن يكون هذا هو نفسه باستثناء أننا نسمي الشيء غير المستخدم _v بدلاً من _ .

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

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

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

صيح. علاوة على ما قلته ، يؤدي let _ = foo() إلى استدعاء drop الفور ، بينما لا يتم إسقاط _v إلا عندما يخرج عن النطاق (مثل v قد يفعل ).

في الواقع ، هذا ما قصدته بعبارة " _ خاص".

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

ما زلت أقترح بعد ذلك أن يكون لديك بوابة ميزة لمنع التحويل من ! إلى قيم rvalues ​​غير الحجم. سيؤدي هذا إلى رفض رمز مثل let _:str = return; ، لكنني لا أعتقد أن أي شخص سيستخدمه في الكود.

طالما أن النوع ! غير مستقر ، يمكننا إجراء تغييرات جذرية على المكان الذي يمكن أو لا يمكن فرضه فيه.

السؤال هو ، إذا قمنا بتثبيته دون إكراه على التوقيت الصيفي من أجل إصلاح https://github.com/rust-lang/rust/issues/49593 ، فهل نريد استعادة هذا الإكراه لاحقًا عندما نضيف قيمًا غير متغيرة؟ أن تكون قادرًا على القيام بذلك دون كسر https://github.com/rust-lang/rust/issues/49593 مرة أخرى؟

اقتراحي السابق يتضمن هذا. # 49593 يجب أن يكون مشكلة حظر لقيم rvalues ​​غير الحجم.

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

FWIW ، _ ليس مثل اسم متغير خاص. إنها صيغة نمط منفصلة تمامًا ولا تفعل شيئًا للمكان المطابق. تعمل مطابقة الأنماط على الأماكن وليس على القيم .
أقرب ما يتعلق بالربط المتغير هو ref _x ، ولكن حتى في ذلك الحين ربما تحتاج إلى NLL لتجنب الاقتراض المتضارب الناتج عنه. هذا يعمل بالمناسبة:

// The type `str` is the type of the place being matched. `x` has type `&str`.
let ref x: str = *"foo";
// Fully equivalent to this:
let x: &str = &*"foo";

eddyb

لم تفهم وجهة نظري. افترض أنه في Rust لا يوجد حاليًا أي قيم غير بحجم يمكن أن تظهر في الكود على الإطلاق. ومع ذلك اتضح أنها تظهر في let _:str = *"" . على الرغم من أن هذه القيمة لا يمكن أن تعيش إلى الأبد ، إلا أنها تعيش في المستوى التركيبي. شيء مثل الشبح ...

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

ومع ذلك اتضح أنها تظهر في اسمحوا _: str = * "". على الرغم من أن هذه القيمة لا يمكن أن تعيش إلى الأبد ، إلا أنها تعيش في المستوى التركيبي. شيء مثل الشبح ...

هناك "شبح" مشابه في تعبير مثل &*foo . أولاً تقوم بإلغاء المرجع foo ثم تأخذ عنوان النتيجة دون تحريكها . لذا فهي ليست مثل & { *foo } ، على سبيل المثال.

earthengineSimonSapin لا rvalues unsized ( "التعبير قيمة") موجودة هناك. فقط lvalues ​​("تعبيرات المكان") تفعل. *"foo" هو مكان ، ومطابقة الأنماط لا تتطلب قيمة ، فقط مكان.

تعتبر الأسماء "lvalue" و "rvalue" من الآثار ومضللة إلى حد ما في لغة C أيضًا ، ولكنها أسوأ في Rust ، لأن RHS let هو "lvalue" (تعبير مكان) ، على الرغم من الاسم.
(تعبيرات التخصيص هي المكان الوحيد الذي تكون فيه أسماء وقواعد C منطقية في Rust)

في الأمثلة الخاصة بك ، let x = *"foo"; ، القيمة مطلوبة لربطها بـ x .
وبالمثل ، يُنشئ &*"foo" تعبير مكان ثم يقترضه ، دون الحاجة إلى إنشاء قيمة غير بحجم ، بينما {*"foo"} هو تعبير قيمة دائمًا وبالتالي لا يسمح بالأنواع غير الحجم.

في https://github.com/rust-lang/rust/pull/52420 أصبت بذلك

let Ok(b): Result<B, !> = ...;
b

لم يعد يعمل. هل هذا متعمد؟

AFAIK نعم - قصة النمط المعصوم معقدة ولها بوابة ميزة منفصلة عن ! نفسها. راجع أيضًا https://github.com/rust-lang/rfcs/pull/1872 و https://github.com/rust-lang/rust/issues/48950.

@ Ericson2314 تحديدًا الميزة التي ستحتاج إلى إضافتها إلى liballoc هي exhaustive_patterns .

شكرا!!

محادثة شيقة حول الأجزاء الداخلية:

إذا كنت ستفعل ذلك ، فلماذا لا تعامل! نفسها كمتغير استنتاج؟

فقط قم بعمل غلاف حول ! بـ PhandomData لإزالة الغموض عن نوع العنصر.

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

تضمينrfcbot fcp

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

https://github.com/rust-lang/rust/pull/50121 ليس فقط أعاد الاستقرار ولكن أيضًا دلالات الرجوع. هل هذا شيء نريد إعادة النظر فيه؟

مربع الاختيار المتبقي غير المحدد في وصف المشكلة هو:

ما السمات التي يجب أن نطبقها مقابل ! ؟ يتضمن PR # 35162 الأولي Ord وعدد قليل من الآخرين. من المحتمل أن تكون هذه مشكلة متعلقة بـ T-libs ، لذلك أقوم بإضافة هذه العلامة إلى المشكلة.

يمكننا إضافة إشارات لاحقًا ، أليس كذلك؟ أم أن هذا مانع؟

تم فتح https://github.com/rust-lang/rust/issues/57012. أتوقع أن يتم تمكين الرجوع إلى ! مرة أخرى كجزء من هذا التغيير ، نعم (على الرغم من دعنا نناقش ذلك بشأن مشكلة التثبيت).

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

يبدو أن هناك خطأ / ثغرة في عدم وجود نوع مطلقًا خلف بوابة ميزة ، ومن الممكن الرجوع إليه على Stable: https://github.com/rust-lang/rust/issues/33417#issuecomment -467053097

تحرير: قدم https://github.com/rust-lang/rust/issues/58733.

إضافة استخدام للنوع في مثال الكود المرتبط أعلاه:

trait MyTrait {
    type Output;
}

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

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

fn main() {
    let _a: Void;
}

يتم تجميع هذا في Rust 1.12.0 والذي أعتقد أنه الأول مع https://github.com/rust-lang/rust/pull/35162. في 1.11.0 ، أخطاء في:

error: the trait bound `fn() -> !: MyTrait` is not satisfied [--explain E0277]
  --> a.rs:12:13
   |>
12 |>     let _a: Void;
   |>             ^^^^
help: the following implementations were found:
help:   <fn() -> T as MyTrait>

error: aborting due to previous error

ما هي حالة هذا؟

على حد علمي ، لم تتغير الحالة بشكل كبير منذ الملخص الخاص بي في https://github.com/rust-lang/rust/issues/57012#issuecomment -467889706.

ما هي حالة هذا؟

hosunrise : تم حظر الموقع حاليًا على https://github.com/rust-lang/rust/issues/67225

قد أكون بعيدًا هنا ، لكن المشكلات المحددة التي تشير إليها مناقشة النسالة (https://github.com/rust-lang/rust/issues/66173) تبدو قابلة للحل إذا كان لكل تعداد فرع ! في نظام الكتابة؟

سألاحظ أن هذا لا ينطبق على المشكلة المذكورة في OP من https://github.com/rust-lang/rust/issues/67225 ، والتي ستظل مشكلة.

قد أكون بعيدًا هنا ولكن المشكلات المحددة التي يذكرها مناقشة النسالة (# 66173) تبدو قابلة للحل إذا كان لكل تعداد فرع ! في نظام النوع؟

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

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