错误:无法移出
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
但第二种情况也应该有效!
如果您将 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
的签名。
这发生在这段代码中:
当我们沿着这条路走下去时,我们永远不会存储这种“起源”。 相比之下,否则会启动的 upvar 推理是这样的:
所以我们可能需要在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(||
| ^^^^^^^^^
|
最有用的评论
@Boscop因为你只能移动一次——
FnMut
闭包可以多次调用。我不完全同意,但我也不反对。 这种错误通常很难决定如何诊断。 这里有一种张力:闭包想要采取行动(移动),但
f
的签名不允许(因为它需要一个可以被多次调用的闭包)。 可能是签名错误(它应该被概括为接受FnOnce
)或者闭包是错误的(它试图做它不应该做的事情)。 至少,我们显然没有很好地捕捉诊断中的这种“紧张”。有趣的是,如果闭包没有直接作为
f
的参数给出,我们会给出一个更好的错误:这是因为@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
存储某种信息,然后改进借用检查。 然后我们可以给出如下错误: