为何出现‘闭包实现FnMut,捕获变量引用无法逃逸’错误及相关疑问
Rust闭包包装动态分发方法的生命周期问题解答
我定义了一个带泛型的Interface trait,包含method方法,需要通过动态分发调用,传递的实例类型是Rc<RefCell<dyn Interface>>。现在要把method的调用包装进闭包,要求调用闭包传入input时,效果等价于调用实现Interface的obj.method(input)。
尝试代码如下:
use std::cell::RefCell; use std::rc::Rc; pub trait Interface<'p, T> { fn method<'s: 'p>(&'p self, input: &'s str) -> T; } fn wrap_interface_with_closure<'p, 's: 'p, T: 'p>( instance: Rc<RefCell<dyn Interface<'p, T>>>, ) -> impl FnMut(&'s str) -> T + 'p { move |input| (*instance).borrow().method(input) }
运行后出现错误:
error[E0716]: temporary value dropped while borrowed | 13 | instance: Rc<RefCell<dyn Interface<'p, T>>>, | -------- lifetime `'2` appears in the type of `instance` 14 | ) -> impl FnMut(&'s str) -> T + 'p { 15 | move |input| (*instance).borrow().method(input) | ^^^^^^^^^^^^^^^^^^^^-------------- | | | | | temporary value is freed at the end of this statement | creates a temporary which is freed while still in use | argument requires that borrow lasts for `'2` error: lifetime may not live long enough | 13 | instance: Rc<RefCell<dyn Interface<'p, T>>>, | -------- lifetime `'2` appears in the type of `instance` 14 | ) -> impl FnMut(&'s str) -> T + 'p { 15 | move |input| (*instance).borrow().method(input) | ------------ ^^^^^^^^ closure capture requires that `'1` must outlive `'2` | | | lifetime `'1` represents this closure's body | = note: closure implements `FnMut`, so references to captured variables can't escape the closure error: aborting due to 2 previous errors; 2 warnings emitted
我的疑问:提示信息closure implements FnMut, so references to captured variables can't escape the closure中所指的捕获变量是哪一个?是instance发生逃逸,还是其借用发生了逃逸?
疑问解答
提示里的捕获变量是instance,但真正出问题的不是instance本身逃逸,而是instance.borrow()返回的Ref临时值的借用试图逃逸闭包。
问题原因分析
(*instance).borrow()会创建一个Ref<dyn Interface<'p, T>>临时对象,这个对象的生命周期只到当前语句结束。- 你的
trait中method方法要求&'p self,也就是self的引用生命周期必须是'p,但临时Ref的生命周期远短于'p,编译器认为这个借用试图存活到闭包之外(违背FnMut的规则:捕获变量的引用不能逃逸闭包)。 FnMut闭包不允许捕获的变量的引用泄露到闭包外部,而这里method的签名要求self的引用生命周期绑定到'p,相当于要求临时Ref的生命周期必须覆盖'p,这显然不可能。
修正方案
调整trait的生命周期约束,让method的self引用生命周期不固定为'p,而是可以更短:
use std::cell::RefCell; use std::rc::Rc; // 修改trait的生命周期定义,让method的self引用生命周期独立于'p pub trait Interface<'p, T> { fn method<'s>(&self, input: &'s str) -> T where 's: 'p; } fn wrap_interface_with_closure<'p, T: 'p>( instance: Rc<RefCell<dyn Interface<'p, T>>>, ) -> impl FnMut(&str) -> T + 'p { move |input| instance.borrow().method(input) }
如果'p是必须绑定到self的生命周期,可以考虑让T不依赖'p,或者根据业务场景调整生命周期绑定逻辑,核心是让临时Ref的生命周期满足method的要求,同时不违反闭包的逃逸规则。
内容的提问来源于stack exchange,提问作者Aaron Stanek
相关产品推荐
相关产品推荐

