我有以下功能:
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
这是一个具有上述所有功能的游乐场。
我认为我对生命周期和所有权没有足够的了解,无法理解为什么每个功能都会失败。
我有点了解map_option_1
和map_option_3
失败,因为move
没有使用显式移动所有权,但我很奇怪map_option_2
和map_option_4
失败。
我很惊讶的是map_option_2
不行的,但实际map_option
功能如何工作。对我来说,这些功能实际上是相同的。
为什么每个map_option_X
函数都无法编译?
我以为我可能需要
move
关闭,以便它拥有所有权a2b
没错,您确实需要move
外部封闭。没有move
,闭包将a2b
通过引用捕获。但是,a2b
是一个局部参数,并且返回引用了局部变量的闭包是无效的。
添加move
到内部闭包会导致错误,因为该函数返回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
为aBox<FnOnce(A) -> B>
并没有帮助,因为实际上它可以在对的许多调用中使用map
。
有一种避免内部闭包的方法:将传递a2b
给map
。这行得通,因为
Box<F> where F: Fn<A>
实施Fn<A>
和&F where F: Fn<A>
实现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
通过引用从外盖捕获。之所以可行,是Fn
因为调用闭包只需要共享引用即可。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句