我有一个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
来接受任意 unboxed 闭包的方法。在这种情况下,我们的方法可以将闭包本身装箱,因此调用站点将变为:
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
接受(任意大小的)闭包作为参数?如果可以,怎么办?
答案 0 :(得分:3)
您可以使用impl Fn接受任意大小的闭包作为参数吗?
是的。
参数位置的 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)
这有效。
另请参阅: