Rust: 間違ったエラー: `FnMut`クロージャでキャプチャされた外部変数から移動できません

作成日 2018年01月29日  ·  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

しかし、2番目のケースも機能するはずです!

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

最も参考になるコメント

@Boscopは、物事を1FnMutクロージャーは複数回呼び出すことができます。

(ところで、エラーはそれが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 (私が思う?またはそれは他の誰かでした...)がクロージャの「定義特性」を決定する理由を追跡するためです。 現在、 f署名のみを理由として、クロージャをFnMut必要があると判断しているため、このチェックは機能していません。

それはこのコードで起こります:

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

私たちがその道を進むとき、私たちはその種の「起源」を保存することになることは決してありません。 対照的に、そうでなければキックインするupvar推論はこれを行います:

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は、物事を1FnMutクロージャーは複数回呼び出すことができます。

(ところで、エラーはそれが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 (私が思う?またはそれは他の誰かでした...)がクロージャの「定義特性」を決定する理由を追跡するためです。 現在、 f署名のみを理由として、クロージャをFnMut必要があると判断しているため、このチェックは機能していません。

それはこのコードで起こります:

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

私たちがその道を進むとき、私たちはその種の「起源」を保存することになることは決してありません。 対照的に、そうでなければキックインするupvar推論はこれを行います:

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 評価