我有一个 History
包含初始状态、当前状态和闭包列表的类型,其中包含从初始状态计算当前状态所需的每个更改。这些应用了 .apply(...)
方法采用盒装闭包,使用它修改当前状态,并将其添加到列表中。因为我希望这些可以确定地重复使用,所以它们是 Fn
, 不是 FnMut
或 FnOnce
.
struct History<State: Clone> {
initial: State,
current: State,
updates: Vec<Box<dyn Fn(&mut State)>>,
}
impl<State: Clone> History<State> {
fn apply(&mut self, update: Box<dyn Fn(&mut State)>) {
update(&mut self.current);
self.updates.push(update);
}
}
我目前将关闭视为 Box<dyn Fn(&mut State)>
, 它工作正常:
fn main() {
let mut history = History::<usize> {
initial: 0,
current: 0,
updates: vec![],
};
let delta = 10;
history.apply(Box::new(move |mut value| *value += delta));
println!("{}", history.current);
}
10
这让我开始思考是否有可能通过使用 impl Trait
来接受任意未装箱 闭包的方法|而不是 dyn Trait
.在这种情况下,我们的方法可以将闭包本身装箱,因此调用站点将变为:
history.apply(move |mut value| *value += delta);
(请考虑这是否可能的问题,即使在这种情况下这不是一个好主意。)
我想象每个闭包站点就像一个不同的数据类型,每次使用时都用封闭的值实例化,所以 impl
Trait 可以为每个隐式闭包专门化方法,就像它为每个显式类型所做的那样。但我不确定 Rust 是否真的那样工作。
但是,当我尝试更改代码时,出现新的生命周期错误:
fn apply(&mut self, update: impl Fn(&mut State)) {
update(&mut self.current);
self.updates.push(Box::new(update));
}
error[E0310]: the parameter type `impl Fn(&mut State)` may not live long enough
--> src/main.rs:10:27
|
10 | self.updates.push(Box::new(update));
| ^^^^^^^^^^^^^^^^
|
note: ...so that the type `impl Fn(&mut State)` will meet its required lifetime bounds
--> src/main.rs:10:27
|
10 | self.updates.push(Box::new(update));
| ^^^^^^^^^^^^^^^^
这让我很困惑。我不确定哪里有可能变坏的引用。
在我看来,整个关闭状态现在都被移到了 apply
中。通过 impl Fn
参数,然后移入 Box
属于self
.但它提示说我不能将内容移到一个盒子里,因为我有一个可能过时的引用,而不仅仅是拥有的数据?我在哪里借东西?当我直接在 main
中装箱时,为什么没有发生这种情况?而不是 apply
?
是否可以使用 impl Fn
接受一个(任意大小的)闭包作为参数?如果是,怎么办?
最佳答案
Can you use impl Fn to accept arbitrarily-sized closures as arguments?
是的。
参数位置中的impl trait
与泛型完全相同。这些是相同的:
fn foo1(_: impl Fn(u8) -> i8) {}
fn foo2<F>(_: F)
where
F: Fn(u8) -> i8,
{}
这实际上是接受闭包(或许多其他特征实现)的普遍首选方式,因为它允许编译器单态化结果并避免任何不必要的间接寻址.
编译你的代码有这个帮助文本(which currently has some rendering glitches):
help: consider adding an explicit lifetime bound `impl Fn(&mut State): 'static`...
|
8 | fn apply(&mut self, update: impl Fn(&mut State): 'static + {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
意思是说加上+'static
:
fn apply(&mut self, update: impl Fn(&mut State) + 'static)
这有效。
另见:
- What makes `impl Trait` as an argument "universal" and as a return value "existential"?
- Why is adding a lifetime to a trait with the plus operator (Iterator<Item = &Foo> + 'a) needed?
- The compiler suggests I add a 'static lifetime because the parameter type may not live long enough, but I don't think that's what I want
关于rust - 您可以使用 impl Fn 接受任意大小的闭包作为参数吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51771465/