我正在开发一个 Rust 项目,我希望存储一个盒装闭包的 Vec 以及一个稍后重用的通用参数。 我想修改每个存储的闭包以添加一些常见的处理,但是我期望能够使用的代码的几个版本存在问题,需要在闭包的通用参数上绑定一些
'static
,我认为这实际上并不是必需的,因为 T
的生命周期应该独立于闭包的生命周期。
所有提供的示例都可以在 rust Playground 中运行,轻松注释和取消注释相关函数以进行测试。 此外,为了清楚起见,所有示例都使用以下类型别名:
pub type MyOp<T> = dyn Fn(&mut String, &mut T);
下面的示例显示了有问题的代码:
fn test<T>() -> Vec<Box<MyOp<T>>> {
let mut operators: Vec<Box<MyOp<T>>> = Vec::default();
let mut add_op = |operator: Box<MyOp<T>>| {
let operator_bis = move |s: &mut _, t: &mut _| {
operator(s, t);
// Do something else with s
};
operators.push(Box::new(operator_bis));
};
add_op(Box::new(|s, t| ()));
add_op(Box::new(|s, t| ()));
operators
}
此代码无法编译,并出现以下错误,这是我没想到的,因为
T
的生命周期不应影响最终盒装闭包的生命周期。
error[E0310]: the parameter type `T` may not live long enough
--> src/main.rs:14:24
|
14 | operators.push(Box::new(operator_bis));
| ^^^^^^^^^^^^^^^^^^^^^^
| |
| the parameter type `T` must be valid for the static lifetime...
| ...so that the type `T` will meet its required lifetime bounds
|
help: consider adding an explicit lifetime bound
|
6 | fn test<T: 'static>() -> Vec<Box<MyOp<T>>> {
| +++++++++
有趣的是,不调用运算符闭包就可以编译,但显然代码并没有达到我最初的目标。
fn test<T>() -> Vec<Box<MyOp<T>>> {
let mut operators: Vec<Box<MyOp<T>>> = Vec::default();
let mut add_op = |operator: Box<MyOp<T>>| {
operators.push(operator);
};
add_op(Box::new(|_s, _rng| ()));
add_op(Box::new(|_s, _rng| ()));
operators
}
下面的示例显示了依赖辅助函数来修改闭包的工作版本。 该版本的优点是可以接受盒装和非盒装封口。
fn add_operator<T>(
operator: impl Fn(&mut String, &mut T) + 'static,
operators: &mut Vec<Box<MyOp<T>>>,
) {
let op = move |s: &mut _, t: &mut _| {
operator(s, t);
// Do something else with s
};
operators.push(Box::new(op));
}
fn test<T>() -> Vec<Box<MyOp<T>>> {
let mut operators = Vec::default();
add_operator(Box::new(|s: &mut _, t: &mut _| ()), &mut operators);
add_operator(|s, t| (), &mut operators);
operators
}
但是,我也希望下面的版本只接受闭包进行编译:
fn add_operator<T>(
operator: Box<MyOp<T>>,
operators: &mut Vec<Box<MyOp<T>>>,
) {
let op = move |s: &mut _ , t: &mut _| {
operator(s, t);
// Do something else with s
};
operators.push(Box::new(op));
}
fn test<T>() -> Vec<Box<MyOp<T>>> {
let mut operators = Vec::default();
add_operator(Box::new(|s: &mut _, t: &mut _| ()), &mut operators);
add_operator(Box::new(|s: &mut _, t: &mut _| ()), &mut operators);
operators
}
我认为这可能与 Rust 存储库上的 this issues 有关,因为编译器无法检测到
T
的生命周期并不真正相关。
但是,我对与闭包相关的生命周期问题的理解并没有那么有信心,在提出问题之前,我想知道是否有人可以查明我的代码中的错误,或者是否所有版本都应该能够编译。
以下编辑后添加信息。
由于到目前为止收到的评论和答案都解释了如何编译示例,考虑到
'static
类型别名上隐藏的 dyn
绑定,我试图想出一个很好的例子来表明我想要T
生命周期独立于函数的生命周期。
目标是能够执行以下示例中的操作,其中该函数应该能够使用任何
Cow
进行调用,但实际上失败了,即使使用我最初认为有效的版本也是如此。
在这种情况下,a
的生命周期即使它不应该发挥作用,因为在调用操作员之后它不会被使用。
fn main() {
let operators_cow = test::<Cow<'_, [u32]>>();
let mut s = String::new();
let a = [10u32, 20];
let mut t = Cow::from(a.as_slice());
operators_cow[0](&mut s, &mut t);
drop(operators_cow); // Does not compile if uncommented
}
正如 cafce25 指出的,
pub type MyOp<T> = dyn Fn(&mut String, &mut T);
相当于 pub type MyOp<T> = dyn Fn(&mut String, &mut T) + 'static;
。您需要为类型别名添加生命周期,并且还需要使用它的函数。
如果你正确地用生命周期注释你的代码,它就会编译:
pub type MyOp<'a, T> = dyn Fn(&mut String, &mut T) + 'a;
fn add_operator<'a, T: 'a>(
operator: Box<MyOp<'a, T>>,
operators: &mut Vec<Box<MyOp<'a, T>>>,
) {
let op = move |s: &mut _ , t: &mut _| {
operator(s, t);
// Do something else with s
};
operators.push(Box::new(op));
}
fn test<'a, T: 'a>() -> Vec<Box<MyOp<'a, T>>> {
let mut operators = Vec::default();
add_operator(Box::new(|s: &mut _, t: &mut _| ()), &mut operators);
add_operator(Box::new(|s: &mut _, t: &mut _| ()), &mut operators);
operators
}