Rust中main末尾销毁的变量为何不满足'static生命周期?
闭包捕获局部变量引发生命周期错误
我尝试传递一个捕获了局部变量的闭包,代码如下:
fn main() { /* snip */ let COIN = some_function_call(); /* snip */ hermes.with_task( u32::MAX, 1, 1, Box::new( |hermes| { let mut rng = rand::thread_rng(); Command::SpawnCommand( COIN, Vec3::new( rng.gen_range(-WORLD_SIZE..WORLD_SIZE), 5.5, rng.gen_range(-WORLD_SIZE..WORLD_SIZE) ) ) } ) ); /* snip */ }
被捕获的变量是COIN。
收到的错误如下:
error[E0597]: `COIN` does not live long enough --> src\main.rs:250:11 | 246 | / Box::new( 247 | | |hermes| { | | -------- value captured here 248 | | let mut rng = rand::thread_rng(); 249 | | Command::SpawnCommand( 250 | | COIN, | | ^^^^ borrowed value does not live long enough ... | 257 | | } 258 | | ) | |_____- cast requires that `COIN` is borrowed for `'static` ... 272 | } | - `COIN` dropped here while still borrowed
with_task方法定义如下:
impl Hermes { /* snip */ pub fn with_task<'a: 'static>(&mut self, repeat: u32, span: u32, offset: u32, f: Box<dyn Fn(&Hermes) -> Command + 'a>) -> usize { let t = TaskInfo { task: f, repeat, span, counter: offset, }; self.tasks.push(t); self.tasks.len() - 1 } /* snip */ }
最让我困惑的是错误提示里的这一行:
272 | } | - `COIN` dropped here while still borrowed
这正是main函数结束的位置,我完全搞不懂问题出在哪。
编辑
以下是可复现代码示例:
struct Foo { tasks: Vec<Box<dyn Fn(&Foo) -> Command>>, } impl Foo { fn with_task(&mut self, f: Box<dyn Fn(&Foo) -> Command>) { self.tasks.push(f); } } enum Command { Com1(usize), } fn main() { let VAR: usize = 0; let mut foo = Foo { tasks: Vec::new(), }; foo.with_task(Box::new( |foo| { Command::Com1(VAR) } )); }
问题原因与解决方法
核心原因
你的闭包被存储到了Hermes(或示例中的Foo)的tasks向量中,而这个向量的生命周期会超过main函数内的COIN(或VAR)变量。
看with_task的定义:'a: 'static要求传入的闭包必须拥有'static生命周期——也就是闭包捕获的所有变量要么是'static类型,要么被闭包拥有所有权(而非借用)。但当前闭包是借用了COIN,当main函数结束时COIN被销毁,闭包却仍存留在hermes.tasks中,这会导致悬垂引用,因此Rust编译器报错阻止。
解决方法
根据COIN的类型,有两种常见修复方式:
让闭包获取变量所有权
如果COIN的类型实现了Clone,可以在捕获时克隆它,让闭包拥有独立副本:Box::new(move |hermes| { // 添加move关键字转移所有权 let mut rng = rand::thread_rng(); Command::SpawnCommand( COIN.clone(), // 克隆变量生成副本 Vec3::new( rng.gen_range(-WORLD_SIZE..WORLD_SIZE), 5.5, rng.gen_range(-WORLD_SIZE..WORLD_SIZE) ) ) })如果类型无需克隆,直接用
move关键字让闭包获取变量所有权即可:Box::new(move |hermes| { // move转移所有权 let mut rng = rand::thread_rng(); Command::SpawnCommand( COIN, Vec3::new( rng.gen_range(-WORLD_SIZE..WORLD_SIZE), 5.5, rng.gen_range(-WORLD_SIZE..WORLD_SIZE) ) ) })将变量转为
'static生命周期
如果COIN的值全局不变,可以将其定义为静态变量:// 注意:静态变量初始化必须是编译期可计算的,若为运行期生成需使用lazy_static等库 static COIN: CoinType = some_function_call();
错误提示解释
错误中提到COIN在main结束时被销毁但仍被借用,是因为闭包被存在hermes.tasks里,而hermes的生命周期(假设在main中创建)与main一致,但闭包借用了COIN,COIN在main结束时销毁,闭包却还存活,这违反了Rust的核心规则——引用的生命周期不能长于被引用变量的生命周期。
内容的提问来源于stack exchange,提问作者Plegeus
相关产品推荐
相关产品推荐

