Rust: خطأ خاطئ: لا يمكن الخروج من المتغير الخارجي الملتقط في إغلاق "FnMut"

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

خطأ: لا يمكن الخروج من المتغير الخارجي الملتقط في إغلاق FnMut

struct Foo {
    a: i32,
    b: i32,
    bar: Bar,
}

struct Bar;

impl Bar {
    fn f<F: FnMut()>(&self, mut f: F) {
        f();
    }
}

fn main() {
    let mut foo = Foo { a: 1, b: 2, bar: Bar };
    let a = &mut foo.a;
    let b = &mut foo.b;
    (|| { // works
        *if true {a} else {b} = 42;
    })();

    let mut foo = Foo { a: 1, b: 2, bar: Bar };
    let a = &mut foo.a;
    let b = &mut foo.b;
    foo.bar.f(|| { // doesn't work
        *if true {a} else {b} = 42;
    });
}

https://play.rust-lang.org/؟gist=4ce6948a92c2fcb281b3cade8574691d&version=nightly

لكن الحالة الثانية يجب أن تعمل أيضًا!

A-borrow-checker A-diagnostics C-enhancement T-compiler

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

Boscop لأنه يمكنك فقط نقل الأشياء مرة واحدة - يمكن استدعاء إغلاق FnMut أكثر من مرة.

(راجع للشغل ، يجب أن يشير الخطأ إلى أنه يجب أن يكون FnOnce بعد ذلك.)

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

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

error[E0525]: expected a closure that implements the `FnMut` trait, but this closure only implements `FnOnce`
  --> src/main.rs:19:19
   |
19 |       let closure = || { // doesn't work
   |  ___________________^
20 | |         *if true {a} else {b} = 42;
21 | |     };
   | |_____^
22 |       foo.bar.f(closure);
   |               - the requirement to implement `FnMut` derives from here
   |
note: closure is `FnOnce` because it moves the variable `a` out of its environment
  --> src/main.rs:20:19
   |
20 |         *if true {a} else {b} = 42;
   |                

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

يحدث ذلك في هذا الكود:

https://github.com/rust-lang/rust/blob/70f7d5842f29d4900f24420b030f144d21f3c5fc/src/librustc_typeck/check/closure.rs#L151 -L155

عندما نسير في هذا الطريق ، لا ينتهي بنا المطاف بتخزين "أصل" هذا النوع. على النقيض من ذلك ، فإن الاستدلال الصعودي الذي كان سيبدأ لولا ذلك يفعل هذا:

https://github.com/rust-lang/rust/blob/70f7d5842f29d4900f24420b030f144d21f3c5fc/src/librustc_typeck/check/upvar.rs#L182 -L188

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

error[E0507]: cannot move out of captured outer variable in an `FnMut` closure
  --> src/main.rs:20:19
   |
17 |     let a = &mut foo.a;
   |         - captured outer variable
...
20 |         *if true {a} else {b} = 42;
   |                   ^ cannot move out of captured outer variable in an `FnMut` closure
note: closure is `FnMut` because of the requirements of `f()`
  --> src/main.rs:19:19
   |
19 |         foo.bar.f(||
   |         ^^^^^^^^^
   |            

ال 3 كومينتر

يعمل إذا قمت بتغيير FnMut إلى FnOnce.

ولكن لماذا لا تعمل أيضًا مع FnMut؟

(راجع للشغل ، يجب أن يشير الخطأ إلى أنه يجب أن يكون FnOnce بعد ذلك.)

Boscop لأنه يمكنك فقط نقل الأشياء مرة واحدة - يمكن استدعاء إغلاق FnMut أكثر من مرة.

(راجع للشغل ، يجب أن يشير الخطأ إلى أنه يجب أن يكون FnOnce بعد ذلك.)

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

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

error[E0525]: expected a closure that implements the `FnMut` trait, but this closure only implements `FnOnce`
  --> src/main.rs:19:19
   |
19 |       let closure = || { // doesn't work
   |  ___________________^
20 | |         *if true {a} else {b} = 42;
21 | |     };
   | |_____^
22 |       foo.bar.f(closure);
   |               - the requirement to implement `FnMut` derives from here
   |
note: closure is `FnOnce` because it moves the variable `a` out of its environment
  --> src/main.rs:20:19
   |
20 |         *if true {a} else {b} = 42;
   |                

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

يحدث ذلك في هذا الكود:

https://github.com/rust-lang/rust/blob/70f7d5842f29d4900f24420b030f144d21f3c5fc/src/librustc_typeck/check/closure.rs#L151 -L155

عندما نسير في هذا الطريق ، لا ينتهي بنا المطاف بتخزين "أصل" هذا النوع. على النقيض من ذلك ، فإن الاستدلال الصعودي الذي كان سيبدأ لولا ذلك يفعل هذا:

https://github.com/rust-lang/rust/blob/70f7d5842f29d4900f24420b030f144d21f3c5fc/src/librustc_typeck/check/upvar.rs#L182 -L188

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

error[E0507]: cannot move out of captured outer variable in an `FnMut` closure
  --> src/main.rs:20:19
   |
17 |     let a = &mut foo.a;
   |         - captured outer variable
...
20 |         *if true {a} else {b} = 42;
   |                   ^ cannot move out of captured outer variable in an `FnMut` closure
note: closure is `FnMut` because of the requirements of `f()`
  --> src/main.rs:19:19
   |
19 |         foo.bar.f(||
   |         ^^^^^^^^^
   |            
هل كانت هذه الصفحة مفيدة؟
0 / 5 - 0 التقييمات