Rust: Falscher Fehler: kann sich nicht aus der erfassten äußeren Variablen in einer `FnMut`-Closing bewegen

Erstellt am 29. Jan. 2018  ·  3Kommentare  ·  Quelle: rust-lang/rust

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!

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

Hilfreichster Kommentar

@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:

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:

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

Alle 3 Kommentare

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:

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:

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(||
   |         ^^^^^^^^^
   |            
War diese Seite hilfreich?
0 / 5 - 0 Bewertungen