为什么“在装箱包含另一个装箱闭包的闭包时,通用闭包参数需要静态生命周期?”

问题描述 投票:0回答:1
我正在开发一个 Rust 项目,我希望存储一个盒装闭包的 Vec 以及一个稍后重用的通用参数。 我想修改每个存储的闭包以添加一些常见的处理,但是我期望能够使用的代码的几个版本存在问题,需要对闭包的通用参数进行一些“静态绑定”,我认为这实际上并不是必需的.

所有提供的示例都可以在

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

 的生命周期并不真正相关。
但是,我对与闭包相关的生命周期问题的理解并没有那么有信心,在提出问题之前,我想知道是否有人可以查明我的代码中的错误,或者是否所有版本都应该能够编译。 

generics rust closures lifetime
1个回答
0
投票
正如 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 }
    
© www.soinside.com 2019 - 2024. All rights reserved.