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

但第二种情况也应该有效!

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

最有用的评论

@Boscop因为你只能移动一次—— FnMut闭包可以多次调用。

(顺便说一句,错误应该说它必须是 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 (我认为?或者是其他人......)进行跟踪以跟踪我们为什么决定闭包的“定义特征”。 现在,这种检查并没有发挥作用,因为我们决定闭包应该是FnMut仅仅是因为f的签名。

这发生在这段代码中:

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因为你只能移动一次—— FnMut闭包可以多次调用。

(顺便说一句,错误应该说它必须是 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 (我认为?或者是其他人......)进行跟踪以跟踪我们为什么决定闭包的“定义特征”。 现在,这种检查并没有发挥作用,因为我们决定闭包应该是FnMut仅仅是因为f的签名。

这发生在这段代码中:

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 等级

相关问题

modsec picture modsec  ·  3评论

nikomatsakis picture nikomatsakis  ·  3评论

behnam picture behnam  ·  3评论

SharplEr picture SharplEr  ·  3评论

pedrohjordao picture pedrohjordao  ·  3评论