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!
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:
Ketika kita menempuh jalan itu, kita tidak pernah menyimpan "asal" semacam itu. Sebaliknya, inferensi upvar yang seharusnya masuk melakukan ini:
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(||
| ^^^^^^^^^
|
Komentar yang paling membantu
@Boscop karena Anda hanya dapat memindahkan barang satu kali -- penutupan
FnMut
dapat dipanggil lebih dari sekali.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 menerimaFnOnce
) 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 :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 tanganf
.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: