Fehler: kann sich in einer
FnMut
Closing nicht aus der erfassten äußeren Variablen heraus bewegen
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
Aber der zweite Fall sollte auch funktionieren!
Es funktioniert, wenn Sie FnMut in FnOnce ändern.
Aber warum geht das nicht auch mit FnMut?
(Übrigens, der Fehler sollte sagen, dass es dann FnOnce sein muss.)
@Boscop, weil Sie Dinge nur einmal verschieben können – eine FnMut
Schließung kann mehr als einmal aufgerufen werden.
(Übrigens, der Fehler sollte sagen, dass es dann FnOnce sein muss.)
Ich stimme nicht ganz zu, aber ich bin auch nicht anderer Meinung . Diese Art von Fehler ist oft schwer zu diagnostizieren. Hier gibt es eine Art Spannung: Die Closure möchte eine Aktion (einen Zug) ausführen, aber die Signatur von f
lässt dies nicht zu (weil sie eine Closure benötigt, die mehr als einmal aufgerufen werden kann). Es kann sein, dass die Signatur fehlerhaft ist (sie sollte verallgemeinert werden, um ein FnOnce
zu akzeptieren) oder dass die Schließung falsch ist (sie versucht, Dinge zu tun, die sie nicht tun sollte). Zumindest gelingt es uns eindeutig nicht, diese „Spannung“ in der Diagnose einzufangen.
Interessanterweise geben wir einen etwas besseren Fehler aus , wenn die Closure nicht direkt als Argument an f
übergeben wird :
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;
|
Dies liegt an der Verfolgung, die @estebank (glaube ich? oder war es jemand anderes ...) einsetzt, um zu verfolgen, warum wir das "definierende Merkmal" einer Schließung festlegen. Im Moment kommt diese Überprüfung nicht ins Spiel, weil wir nur aufgrund der Unterschrift von f
entscheiden, dass die Schließung FnMut
f
.
Das passiert in diesem Code:
Wenn wir diesen Weg gehen, werden wir nie den "Ursprung" dieser Art speichern. Im Gegensatz dazu macht die Upvar-Inferenz, die sonst eintreten würde, Folgendes:
Wir müssen also wahrscheinlich irgendeine Art von Informationen in closure_kind_origins_mut
speichern und dann die Kreditprüfung verbessern. Dann könnten wir einen Fehler wie:
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(||
| ^^^^^^^^^
|
Hilfreichster Kommentar
@Boscop, weil Sie Dinge nur einmal verschieben können – eine
FnMut
Schließung kann mehr als einmal aufgerufen werden.Ich stimme nicht ganz zu, aber ich bin auch nicht anderer Meinung . Diese Art von Fehler ist oft schwer zu diagnostizieren. Hier gibt es eine Art Spannung: Die Closure möchte eine Aktion (einen Zug) ausführen, aber die Signatur von
f
lässt dies nicht zu (weil sie eine Closure benötigt, die mehr als einmal aufgerufen werden kann). Es kann sein, dass die Signatur fehlerhaft ist (sie sollte verallgemeinert werden, um einFnOnce
zu akzeptieren) oder dass die Schließung falsch ist (sie versucht, Dinge zu tun, die sie nicht tun sollte). Zumindest gelingt es uns eindeutig nicht, diese „Spannung“ in der Diagnose einzufangen.Interessanterweise geben wir einen etwas besseren Fehler aus , wenn die Closure nicht direkt als Argument an
f
übergeben wird :Dies liegt an der Verfolgung, die @estebank (glaube ich? oder war es jemand anderes ...) einsetzt, um zu verfolgen, warum wir das "definierende Merkmal" einer Schließung festlegen. Im Moment kommt diese Überprüfung nicht ins Spiel, weil wir nur aufgrund der Unterschrift von
f
entscheiden, dass die SchließungFnMut
f
.Das passiert in diesem Code:
https://github.com/rust-lang/rust/blob/70f7d5842f29d4900f24420b030f144d21f3c5fc/src/librustc_typeck/check/closure.rs#L151 -L155
Wenn wir diesen Weg gehen, werden wir nie den "Ursprung" dieser Art speichern. Im Gegensatz dazu macht die Upvar-Inferenz, die sonst eintreten würde, Folgendes:
https://github.com/rust-lang/rust/blob/70f7d5842f29d4900f24420b030f144d21f3c5fc/src/librustc_typeck/check/upvar.rs#L182 -L188
Wir müssen also wahrscheinlich irgendeine Art von Informationen in
closure_kind_origins_mut
speichern und dann die Kreditprüfung verbessern. Dann könnten wir einen Fehler wie: