为何Rust对已释放的可变借用报错却忽略不可变借用?
问题解析:带显式生命周期的可变引用编译错误原因与解决办法
问题重现
以下两个函数仅在引用可变性上有区别,但返回可变引用的版本编译报错:
// 编译正常 fn foo<'a>() { let _: &'a () = &(); } // 编译报错 fn foo_mut<'a>() { let _: &'a mut () = &mut (); }
错误信息
error[E0716]: temporary value dropped while borrowed --> src/main.rs:14:30 | 13 | fn foo_mut<'a>() { | -- lifetime `'a` defined here 14 | let _: &'a mut () = &mut (); | ---------- ^^ creates a temporary which is freed while still in use | | | type annotation requires that borrow lasts for `'a` 15 | } | - temporary value is freed at the end of this statement For more information about this error, try `rustc --explain E0716`. error: could not compile `playground` due to previous error
另外,无显式生命周期的可变引用代码可正常编译:
// 编译正常 fn foo_mut_without_lifetime() { let _: &mut () = &mut (); }
原因分析
这是Rust临时值生命周期规则与可变引用特性共同作用的结果:
- 不可变引用的临时值延长规则:对于
&()这类不可变引用,当它被绑定到带有显式生命周期的变量时,Rust会自动延长临时值的生命周期,使其匹配显式生命周期'a的范围,因此foo函数能通过编译。 - 可变引用无临时值延长规则:可变引用要求严格的独占性,Rust为了避免悬垂引用和数据竞争风险,不允许随意延长临时可变值的生命周期。在
foo_mut中,&mut ()创建的临时值会在当前语句结束后立即销毁,但代码要求它的生命周期必须覆盖'a('a由调用者决定,可能远长于临时值的存活时间),因此编译器判定临时值被提前释放,抛出错误。 - 自动推导生命周期的情况:
foo_mut_without_lifetime中,编译器会自动推导引用的生命周期为临时值的存活时间(即当前语句内),不存在生命周期不匹配的问题,因此编译正常。
解决办法
根据实际需求选择对应的处理方式:
- 依赖编译器自动推导生命周期:如果不需要让引用存活到函数外部,直接去掉显式生命周期,像
foo_mut_without_lifetime那样编写代码即可。 - 使用拥有足够生命周期的变量:如果确实需要绑定到显式生命周期的可变引用,要确保引用指向的变量生命周期不短于
'a。比如在函数内部创建一个持久化的变量:
注意:这里fn foo_mut<'a>() { let mut val = (); let _: &'a mut () = &mut val; }'a的生命周期不能超过val的存活范围(即函数内部),如果'a是外部传入的长生命周期,这种写法依然会报错,此时需要重新设计逻辑,避免将短生命周期变量绑定到长生命周期引用。 - 使用静态可变值(需unsafe操作):如果需要一个全局的、生命周期为
'static的可变引用,可以定义静态变量:
注意:静态可变引用存在线程安全风险,必须在static mut EMPTY: () = (); fn foo_mut<'a>() { let _: &'a mut () = unsafe { &mut EMPTY }; }unsafe块中操作,实际使用需谨慎。
内容的提问来源于stack exchange,提问作者Liam Bloom
相关产品推荐
相关产品推荐

