如何解耦Rust闭包泛型参数与闭包自身的生命周期?
我正在开发一个Rust项目,需要存储带泛型参数的boxed闭包Vec以供后续复用。我想修改这些存储的闭包以添加通用处理逻辑,但多版预期可行的代码均出现问题,要求闭包泛型参数添加'static绑定——而我认为这并非必需,因为T的生命周期应独立于闭包自身。
所有示例均使用以下类型别名简化代码:
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的生命周期不应影响最终boxed闭包:
error[E0310]: the parameter type
Tmay not live long enough
--> src/main.rs:14:24
|
operators.push(Box::new(operator_bis)); ^^^^^^^^^^^^^^^^^^^^^^ the parameter type Tmust be valid for the static lifetime......so that the type Twill meet its required lifetime boundshelp: consider adding an explicit lifetime bound
|
6 | fn test<T: 'static>() -> Vec<Box<MyOp>> {
| +++++++++
编译通过但无法实现目标的代码
有趣的是,若不调用operator闭包,代码可编译,但显然无法实现我的初始目标:
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 }
可行的辅助函数版本
以下是借助辅助函数修改闭包的可行版本,该版本支持boxed与非boxed闭包:
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 }
预期可行但编译失败的boxed-only版本
但我认为以下仅接受boxed闭包的版本也应能编译,却未能成功:
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 }
我认为这可能是因为编译器无法识别T的生命周期实际并无关联导致的,但我对闭包相关的生命周期问题理解尚不确定,在提交issue前,希望有人能指出代码中的错误,或确认所有版本本应均可编译。
进一步的场景示例
鉴于此前的讨论均围绕dyn类型别名的隐式'static绑定讲解编译通过的方法,我尝试构造示例以展示我期望T的生命周期独立于函数:
目标是实现如下示例中的操作——函数应能接受任意Cow,但即使使用我原本认为可行的版本,代码仍编译失败。此场景中,a的生命周期本不应影响后续操作,因为调用operator后已不再使用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); // 取消注释后无法编译 }
内容的提问来源于stack exchange,提问作者philher

