Rust: Kesalahan salah: tidak dapat keluar dari variabel luar yang ditangkap dalam penutupan `FnMut`

Dibuat pada 29 Jan 2018  ·  3Komentar  ·  Sumber: rust-lang/rust

kesalahan: tidak dapat keluar dari variabel luar yang ditangkap dalam penutupan 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

Tetapi kasus kedua juga harus berhasil!

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

Komentar yang paling membantu

@Boscop karena Anda hanya dapat memindahkan barang satu kali -- penutupan FnMut dapat dipanggil lebih dari sekali.

(Btw, kesalahannya harus mengatakan itu harus FnOnce.)

Saya tidak sepenuhnya setuju, tetapi saya juga tidak setuju . Kesalahan semacam ini seringkali sulit untuk diputuskan bagaimana mendiagnosis. Ada semacam ketegangan di sini: penutupan ingin mengambil tindakan (gerakan), tetapi tanda tangan f tidak mengizinkannya (karena memerlukan penutupan yang dapat dipanggil lebih dari sekali). Mungkin tanda tangan salah (harus digeneralisasi untuk menerima FnOnce ) atau penutupannya salah (ia mencoba melakukan hal-hal yang seharusnya tidak dilakukan). Minimal, kami jelas tidak melakukan pekerjaan yang baik untuk menangkap 'ketegangan' ini dalam diagnostik.

Menariknya, jika penutupan tidak diberikan secara langsung sebagai argumen ke f kami memberikan kesalahan yang agak lebih baik :

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;
   |                

Ini karena pelacakan yang dilakukan oleh @estebank (saya pikir? atau apakah itu orang lain...) untuk melacak mengapa kami memutuskan "sifat yang menentukan" penutupan. Saat ini, pemeriksaan itu tidak berlaku karena kami memutuskan bahwa penutupan harus FnMut semata-mata karena tanda tangan f .

Itu terjadi dalam kode ini:

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

Ketika kita menempuh jalan itu, kita tidak pernah menyimpan "asal" semacam itu. Sebaliknya, inferensi upvar yang seharusnya masuk melakukan ini:

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

Jadi kita mungkin perlu menyimpan beberapa jenis informasi di closure_kind_origins_mut dan kemudian meningkatkan cek pinjaman. Maka kita bisa memberikan kesalahan seperti:

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(||
   |         ^^^^^^^^^
   |            

Semua 3 komentar

Ini berfungsi jika Anda mengubah FnMut menjadi FnOnce.

Tetapi mengapa itu juga tidak berfungsi dengan FnMut?

(Btw, kesalahannya harus mengatakan itu harus FnOnce.)

@Boscop karena Anda hanya dapat memindahkan barang satu kali -- penutupan FnMut dapat dipanggil lebih dari sekali.

(Btw, kesalahannya harus mengatakan itu harus FnOnce.)

Saya tidak sepenuhnya setuju, tetapi saya juga tidak setuju . Kesalahan semacam ini seringkali sulit untuk diputuskan bagaimana mendiagnosis. Ada semacam ketegangan di sini: penutupan ingin mengambil tindakan (gerakan), tetapi tanda tangan f tidak mengizinkannya (karena memerlukan penutupan yang dapat dipanggil lebih dari sekali). Mungkin tanda tangan salah (harus digeneralisasi untuk menerima FnOnce ) atau penutupannya salah (ia mencoba melakukan hal-hal yang seharusnya tidak dilakukan). Minimal, kami jelas tidak melakukan pekerjaan yang baik untuk menangkap 'ketegangan' ini dalam diagnostik.

Menariknya, jika penutupan tidak diberikan secara langsung sebagai argumen ke f kami memberikan kesalahan yang agak lebih baik :

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;
   |                

Ini karena pelacakan yang dilakukan oleh @estebank (saya pikir? atau apakah itu orang lain...) untuk melacak mengapa kami memutuskan "sifat yang menentukan" penutupan. Saat ini, pemeriksaan itu tidak berlaku karena kami memutuskan bahwa penutupan harus FnMut semata-mata karena tanda tangan f .

Itu terjadi dalam kode ini:

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

Ketika kita menempuh jalan itu, kita tidak pernah menyimpan "asal" semacam itu. Sebaliknya, inferensi upvar yang seharusnya masuk melakukan ini:

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

Jadi kita mungkin perlu menyimpan beberapa jenis informasi di closure_kind_origins_mut dan kemudian meningkatkan cek pinjaman. Maka kita bisa memberikan kesalahan seperti:

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(||
   |         ^^^^^^^^^
   |            
Apakah halaman ini membantu?
0 / 5 - 0 peringkat