为什么不能将变量移出闭包?
Why can't a variable be moved out of a closure?
我有以下功能:
pub fn map_option<A: 'static, B: 'static> (a2b: Box<Fn(A) -> B>) -> Box<Fn(Option<A>) -> Option<B>> {
Box::new(move |opt_a: Option<A>| {
opt_a.map(|a| a2b(a))
})
}
不过,这个写起来还是挺难的。我从更简单但不起作用的东西开始,但我不明白为什么它不起作用。
这是我的第一个版本:
pub fn map_option_1<A: 'static, B: 'static> (a2b: Box<Fn(A) -> B>) -> Box<Fn(Option<A>) -> Option<B>> {
Box::new(|opt_a: Option<A>| {
opt_a.map(a2b)
})
}
这给了我以下错误:
error[E0507]: cannot move out of `a2b`, a captured variable in an `Fn` closure
--> src/lib.rs:11:19
|
9 | pub fn map_option_1<A: 'static, B: 'static> (a2b: Box<Fn(A) -> B>) -> Box<Fn(Option<A>) -> Option<B>> {
| --- captured outer variable
10 | Box::new(|opt_a: Option<A>| {
11 | opt_a.map(a2b)
| ^^^ move occurs because `a2b` has type `std::boxed::Box<dyn std::ops::Fn(A) -> B>`, which does not implement the `Copy` trait
我认为我可能需要 move
闭包,以便它获得 a2b
:
的所有权
pub fn map_option_2<A: 'static, B: 'static> (a2b: Box<Fn(A) -> B>) -> Box<Fn(Option<A>) -> Option<B>> {
Box::new(move |opt_a: Option<A>| {
opt_a.map(a2b)
})
}
然而,这也没有奏效。它失败并显示以下消息:
error[E0507]: cannot move out of `a2b`, a captured variable in an `Fn` closure
--> src/lib.rs:17:19
|
15 | pub fn map_option_2<A: 'static, B: 'static> (a2b: Box<Fn(A) -> B>) -> Box<Fn(Option<A>) -> Option<B>> {
| --- captured outer variable
16 | Box::new(move |opt_a: Option<A>| {
17 | opt_a.map(a2b)
| ^^^ move occurs because `a2b` has type `std::boxed::Box<dyn std::ops::Fn(A) -> B>`, which does not implement the `Copy` trait
这条错误消息说 a2b
没有实现 Copy
,我想这很有道理,但我不知道如何解决它。
出于绝望,我尝试了以下方法:
pub fn map_option_3<A: 'static, B: 'static> (a2b: Box<Fn(A) -> B>) -> Box<Fn(Option<A>) -> Option<B>> {
Box::new(|opt_a: Option<A>| {
opt_a.map(|a| a2b(a))
})
}
这至少给了我一个不同的错误:
error[E0373]: closure may outlive the current function, but it borrows `a2b`, which is owned by the current function
--> src/lib.rs:22:14
|
22 | Box::new(|opt_a: Option<A>| {
| ^^^^^^^^^^^^^^^^^^ may outlive borrowed value `a2b`
23 | opt_a.map(|a| a2b(a))
| --- `a2b` is borrowed here
|
note: closure is returned here
--> src/lib.rs:22:5
|
22 | / Box::new(|opt_a: Option<A>| {
23 | | opt_a.map(|a| a2b(a))
24 | | })
| |______^
help: to force the closure to take ownership of `a2b` (and any other referenced variables), use the `move` keyword
|
22 | Box::new(move |opt_a: Option<A>| {
| ^^^^^^^^^^^^^^^^^^^^^^^
我想所有权问题是有道理的。这就是促使我找到上述实际可行的解决方案的原因。
我试过的另一件事如下:
pub fn map_option_4<A: 'static, B: 'static> (a2b: Box<Fn(A) -> B>) -> Box<Fn(Option<A>) -> Option<B>> {
Box::new(|opt_a: Option<A>| {
opt_a.map(move |a| a2b(a))
})
}
这给了我以下错误:
error[E0507]: cannot move out of `a2b`, a captured variable in an `Fn` closure
--> src/lib.rs:29:19
|
27 | pub fn map_option_4<A: 'static, B: 'static> (a2b: Box<Fn(A) -> B>) -> Box<Fn(Option<A>) -> Option<B>> {
| --- captured outer variable
28 | Box::new(|opt_a: Option<A>| {
29 | opt_a.map(move |a| a2b(a))
| ^^^^^^^^ ---
| | |
| | move occurs because `a2b` has type `std::boxed::Box<dyn std::ops::Fn(A) -> B>`, which does not implement the `Copy` trait
| | move occurs due to use in closure
| move out of `a2b` occurs here
这里有一个 playground,每个函数都有。
我认为我对生命周期和所有权的理解不够好,无法理解为什么这些函数中的每一个都失败了。
我可以理解 map_option_1
和 map_option_3
是如何失败的,因为 move
没有被用来明确移动所有权,但令我惊讶的是 map_option_2
和map_option_4
失败。
我非常惊讶map_option_2
不起作用,但实际map_option
功能起作用。对我来说,这些实际上是相同的功能。
为什么每个map_option_X
函数都编译失败??
I thought that I might need to move
the closure, so that it gets ownership of a2b
没错,您确实需要在外封口上加上 move
。如果没有 move
,闭包将通过引用捕获 a2b
。但是,a2b
是局部参数,返回引用局部参数的闭包是无效的。
将 move
添加到内部闭包会导致错误,因为函数 returns 是 Fn
闭包。对于这个论点,让我们考虑这个 map_option_5
函数:
pub fn map_option_5<A: 'static, B: 'static> (a2b: Box<Fn(A) -> B>) -> Box<Fn(Option<A>) -> Option<B>> {
Box::new(move |opt_a: Option<A>| {
opt_a.map(move |a| a2b(a))
})
}
如果内部闭包按值捕获 a2b
并且外部闭包也是 move
闭包,那么两个闭包最终都会按值捕获 a2b
。根据所有权规则,一次只有一个闭包可能拥有 a2b
,因此当外部闭包被调用时,它将 a2b
移出自身(解构外部闭包)并进入内部闭包(这仅适用于 FnOnce
闭包,因为它们采用 self
而不是 &mut self
或 &self
)。错误消息的原因是我们返回的是 Fn
闭包,而不是 FnOnce
闭包。我们确实可以通过返回一个 FnOnce
闭包来解决这个问题(但是它不能被调用超过一次):
pub fn map_option_5a<A: 'static, B: 'static> (a2b: Box<Fn(A) -> B>) -> Box<FnOnce(Option<A>) -> Option<B>> {
Box::new(move |opt_a: Option<A>| {
opt_a.map(move |a| a2b(a))
})
}
现在,让我们讨论一下为什么 map_option
有效而 map_option_2
无效。问题源于 Option::map
拥有闭包参数的事实。因此,我们最终会遇到类似于上述 map_option_5
的情况。 Option::map
取一个FnOnce
,因为它最多只需要调用一次。将 a2b
更改为 Box<FnOnce(A) -> B>
并没有帮助,因为它实际上可以用于对 map
.
的许多调用
有一种方法可以避免内部闭包:将对 a2b
的引用传递给 map
。这是有效的,因为
Box<F> where F: Fn<A>
implements Fn<A>
和
&F where F: Fn<A>
implements FnOnce<A>
(还有 Fn<A>
,尽管这里无关紧要)。
pub fn map_option_2a<A: 'static, B: 'static> (a2b: Box<Fn(A) -> B>) -> Box<Fn(Option<A>) -> Option<B>> {
Box::new(move |opt_a: Option<A>| {
opt_a.map(&a2b)
})
}
闭包仍然拥有 a2b
的所有权,但在调用时不会消耗它,因此可以多次调用闭包。
map_option
之所以有效,是因为它的外部闭包不需要消耗 a2b
。内部闭包通过引用从外部闭包捕获 a2b
。这是可行的,因为 calling an Fn
闭包只需要对闭包的共享引用。
我有以下功能:
pub fn map_option<A: 'static, B: 'static> (a2b: Box<Fn(A) -> B>) -> Box<Fn(Option<A>) -> Option<B>> {
Box::new(move |opt_a: Option<A>| {
opt_a.map(|a| a2b(a))
})
}
不过,这个写起来还是挺难的。我从更简单但不起作用的东西开始,但我不明白为什么它不起作用。
这是我的第一个版本:
pub fn map_option_1<A: 'static, B: 'static> (a2b: Box<Fn(A) -> B>) -> Box<Fn(Option<A>) -> Option<B>> { Box::new(|opt_a: Option<A>| { opt_a.map(a2b) }) }
这给了我以下错误:
error[E0507]: cannot move out of `a2b`, a captured variable in an `Fn` closure --> src/lib.rs:11:19 | 9 | pub fn map_option_1<A: 'static, B: 'static> (a2b: Box<Fn(A) -> B>) -> Box<Fn(Option<A>) -> Option<B>> { | --- captured outer variable 10 | Box::new(|opt_a: Option<A>| { 11 | opt_a.map(a2b) | ^^^ move occurs because `a2b` has type `std::boxed::Box<dyn std::ops::Fn(A) -> B>`, which does not implement the `Copy` trait
我认为我可能需要
的所有权move
闭包,以便它获得a2b
:pub fn map_option_2<A: 'static, B: 'static> (a2b: Box<Fn(A) -> B>) -> Box<Fn(Option<A>) -> Option<B>> { Box::new(move |opt_a: Option<A>| { opt_a.map(a2b) }) }
然而,这也没有奏效。它失败并显示以下消息:
error[E0507]: cannot move out of `a2b`, a captured variable in an `Fn` closure --> src/lib.rs:17:19 | 15 | pub fn map_option_2<A: 'static, B: 'static> (a2b: Box<Fn(A) -> B>) -> Box<Fn(Option<A>) -> Option<B>> { | --- captured outer variable 16 | Box::new(move |opt_a: Option<A>| { 17 | opt_a.map(a2b) | ^^^ move occurs because `a2b` has type `std::boxed::Box<dyn std::ops::Fn(A) -> B>`, which does not implement the `Copy` trait
这条错误消息说
a2b
没有实现Copy
,我想这很有道理,但我不知道如何解决它。出于绝望,我尝试了以下方法:
pub fn map_option_3<A: 'static, B: 'static> (a2b: Box<Fn(A) -> B>) -> Box<Fn(Option<A>) -> Option<B>> { Box::new(|opt_a: Option<A>| { opt_a.map(|a| a2b(a)) }) }
这至少给了我一个不同的错误:
error[E0373]: closure may outlive the current function, but it borrows `a2b`, which is owned by the current function --> src/lib.rs:22:14 | 22 | Box::new(|opt_a: Option<A>| { | ^^^^^^^^^^^^^^^^^^ may outlive borrowed value `a2b` 23 | opt_a.map(|a| a2b(a)) | --- `a2b` is borrowed here | note: closure is returned here --> src/lib.rs:22:5 | 22 | / Box::new(|opt_a: Option<A>| { 23 | | opt_a.map(|a| a2b(a)) 24 | | }) | |______^ help: to force the closure to take ownership of `a2b` (and any other referenced variables), use the `move` keyword | 22 | Box::new(move |opt_a: Option<A>| { | ^^^^^^^^^^^^^^^^^^^^^^^
我想所有权问题是有道理的。这就是促使我找到上述实际可行的解决方案的原因。
我试过的另一件事如下:
pub fn map_option_4<A: 'static, B: 'static> (a2b: Box<Fn(A) -> B>) -> Box<Fn(Option<A>) -> Option<B>> { Box::new(|opt_a: Option<A>| { opt_a.map(move |a| a2b(a)) }) }
这给了我以下错误:
error[E0507]: cannot move out of `a2b`, a captured variable in an `Fn` closure --> src/lib.rs:29:19 | 27 | pub fn map_option_4<A: 'static, B: 'static> (a2b: Box<Fn(A) -> B>) -> Box<Fn(Option<A>) -> Option<B>> { | --- captured outer variable 28 | Box::new(|opt_a: Option<A>| { 29 | opt_a.map(move |a| a2b(a)) | ^^^^^^^^ --- | | | | | move occurs because `a2b` has type `std::boxed::Box<dyn std::ops::Fn(A) -> B>`, which does not implement the `Copy` trait | | move occurs due to use in closure | move out of `a2b` occurs here
这里有一个 playground,每个函数都有。
我认为我对生命周期和所有权的理解不够好,无法理解为什么这些函数中的每一个都失败了。
我可以理解 map_option_1
和 map_option_3
是如何失败的,因为 move
没有被用来明确移动所有权,但令我惊讶的是 map_option_2
和map_option_4
失败。
我非常惊讶map_option_2
不起作用,但实际map_option
功能起作用。对我来说,这些实际上是相同的功能。
为什么每个map_option_X
函数都编译失败??
I thought that I might need to
move
the closure, so that it gets ownership ofa2b
没错,您确实需要在外封口上加上 move
。如果没有 move
,闭包将通过引用捕获 a2b
。但是,a2b
是局部参数,返回引用局部参数的闭包是无效的。
将 move
添加到内部闭包会导致错误,因为函数 returns 是 Fn
闭包。对于这个论点,让我们考虑这个 map_option_5
函数:
pub fn map_option_5<A: 'static, B: 'static> (a2b: Box<Fn(A) -> B>) -> Box<Fn(Option<A>) -> Option<B>> {
Box::new(move |opt_a: Option<A>| {
opt_a.map(move |a| a2b(a))
})
}
如果内部闭包按值捕获 a2b
并且外部闭包也是 move
闭包,那么两个闭包最终都会按值捕获 a2b
。根据所有权规则,一次只有一个闭包可能拥有 a2b
,因此当外部闭包被调用时,它将 a2b
移出自身(解构外部闭包)并进入内部闭包(这仅适用于 FnOnce
闭包,因为它们采用 self
而不是 &mut self
或 &self
)。错误消息的原因是我们返回的是 Fn
闭包,而不是 FnOnce
闭包。我们确实可以通过返回一个 FnOnce
闭包来解决这个问题(但是它不能被调用超过一次):
pub fn map_option_5a<A: 'static, B: 'static> (a2b: Box<Fn(A) -> B>) -> Box<FnOnce(Option<A>) -> Option<B>> {
Box::new(move |opt_a: Option<A>| {
opt_a.map(move |a| a2b(a))
})
}
现在,让我们讨论一下为什么 map_option
有效而 map_option_2
无效。问题源于 Option::map
拥有闭包参数的事实。因此,我们最终会遇到类似于上述 map_option_5
的情况。 Option::map
取一个FnOnce
,因为它最多只需要调用一次。将 a2b
更改为 Box<FnOnce(A) -> B>
并没有帮助,因为它实际上可以用于对 map
.
有一种方法可以避免内部闭包:将对 a2b
的引用传递给 map
。这是有效的,因为
Box<F> where F: Fn<A>
implementsFn<A>
和&F where F: Fn<A>
implementsFnOnce<A>
(还有Fn<A>
,尽管这里无关紧要)。
pub fn map_option_2a<A: 'static, B: 'static> (a2b: Box<Fn(A) -> B>) -> Box<Fn(Option<A>) -> Option<B>> {
Box::new(move |opt_a: Option<A>| {
opt_a.map(&a2b)
})
}
闭包仍然拥有 a2b
的所有权,但在调用时不会消耗它,因此可以多次调用闭包。
map_option
之所以有效,是因为它的外部闭包不需要消耗 a2b
。内部闭包通过引用从外部闭包捕获 a2b
。这是可行的,因为 calling an Fn
闭包只需要对闭包的共享引用。